顶点坐标变换

5/20/2008 5:38:31 AM

通过坐标变换将3D空间的图元转换成2D图元的过程:主要为世界变换->视图变换->投影变换->视口变换
3D场景中的任何物体,都是由一个一个三角形组成的。而三角形位置信息的就是其各个顶点的三维坐标。这是用来在模型中存储的,而要把物体显示在屏幕上,还需要将它们转换成显示器上的二维坐标。这就需要对每个点实施一套 3D to 2D 的转换公式,在Direct3D中叫做“几何流水线”(Geometry Pipeline)。
一般创建的mesh处于自己的局部坐标系。基本是屏幕正中放置。
世界矩阵:而游戏需要先放入世界坐标系。世界坐标系主要3大功能:平移、旋转、缩放
平移:  D3DXMatrixTranslation() 位于矩阵第四行
1   0   0   0
0   1   0   0
0   0   1   0
Tx Ty Tz  1

旋转: D3DXMatrixRotationX()  延 x 轴旋转
1    0    0    0
0  cos  sin   0
0 -sin   cos  0
0    0    0    1
D3DXMatrixRotationY()  延 y 轴旋转
cos 0  -sin   0
0    1    0    0
sin  0   cos  0
0    0    0    1
D3DXMatrixRotationZ()  延 z 轴旋转
cos sin   0   0
-sin cos  0  0
0    0    1    0
0    0    0    1
缩放: D3DXMatrixScaling

顺序如先旋转R在缩放S 则矩阵为 R*S 。对于行向量(dx默认为行向量)复合矩阵视觉效果为从左到右的顺序各单独矩阵效果的组合。对于列向量则相反: 复合矩阵应为 S * R.视觉效果从右到左(这是opengl 采用的)

观察矩阵:函数 D3DXMatrixLookAtLH()
不要被这个函数吓倒。其实观察矩阵作用和世界矩阵差不多,只是起到旋转、平移的作用。而且是把世界坐标系的物体映射到观察坐标系。只要用摄像机当前世界变换矩阵的逆就好了
矩阵基本组成 M = T*Rz*Ry*Rz 。 T 平移。 Rz 延z旋转。Ry 延Y旋转 Rx 延X轴旋转
实际上可以先获得摄像机的世界坐标 p,和摄像机坐标系轴在世界坐标中的矢量D(看向),U(上),R(右)。然后要把世界坐标的摄像机位置点换算到摄像机局部坐标系中。 (-D * p, - U * p, - R * p)  计算-p 在(D,U,R)矢量上的投影转换为p1.
Dx, Ux,  Rx,  0
Dy, Uy,  Ry, 0
Dz, Uz,  Rz,  0
p1x,p1yp1z, 1

对于D,U,R 其实就是摄像机的世界坐标旋转变换的逆矩阵。如下:
Dx,Dy,Dz
Ux,Uy,Uz
Rx,Ry,Rz
旋转矩阵(还有镜像矩阵)为正交矩阵(参考3D数学基础中的矩阵正交化章节)。对于一个正交矩阵他的逆矩阵等于转置矩阵

这些不会影响mesh 的顶点和法线关系
使用shader时。如计算顶点和法线的点积,没必要映射到观察坐标系.在 direct3d 游戏编程基础的 shader 章节里。顶点法线和光线矢量都映射到了视图空间。其实是没有必要的

而世界变化和观察变换是可以和到一起组成世界-视图变换矩阵

投影变换:把3d 投影到2d平面(似乎说映射[-1,-1,0(opengl 为-1)] 到[1,1,1]的立方体更好一些。但透视投影相当于3D变换成2D,近大远小的变换就在这个时刻)分为透视投影和正交投影。正交投影不会随物体远近影响物体投影的大小。而透视则z值影响,物体越远成像越小。
投影变换涉及缩放和平移操作。对于正交投影矩阵缩放和z值无关。对于透视投影缩放和z值相关。
对于dx 左手坐标系函数如下:

正交投影函数:
D3DXMatrixOrthoLH() 这个是以屏幕中心为坐标原点的。
如果设置2d坐标系。 可以考虑使用 D3DXMatrixOrthoOffCenterLH 函数
D3DXMatrixOrthoOffCenterLH(&m_matProj,0,(float)m_iWidth,(float)m_iHeight,0,0.0f,1.0f);
这样左上角为坐标(0,0). 右下角为(width,height)
透视投影:
D3DXMatrixPerspectiveFovLH()
对于Opengl经过投影变换x,y,z映射到范围[-1,1]. x,y 还保持原来的符号。对于z 最远面 f 映射为1,最近面 n 映射为 -1.
但directx z 值略有差异。z 映射范围为[0,1]。最远面映射为1。最近面映射为0.
对于z值都是以 1/z 映射到 [0,1]或者[-1,1] 范围。因为一般要求近处z值判断要精细一些。而远处精度要小一些。所以用1/z来判断更好。
一个dx 投影矩阵如下: w,h 为 zn 面的宽度和高度。zn 近表面距离。zf 为远表面。

2*zn/w  0       0              00       2*zn/h  0              00       0       zf/(zf-zn)     10       0       zn*zf/(zn-zf)  0


其次坐标一个点p(px,py,pz,1) 经过这个变化成为(x'pz,y'pz,z'pz,pz)

dx 一直透视投影函数一直默认了中心点为[0,0] (摄像机坐标x,y). 如果视景截头体不以[0,0]为中心则需要自己生成投影矩阵。
如对于zn平面,左l 右 r 上 t 下 b.这样 w = r - l  h = t - b .建立的非对称投影矩阵如下(相对于dx左手坐标,对于opengl略有不同)

2*zn/r-l  0         0              00         2*zn/t-b  0              0r+l/r-l   t+b/t-b   zf/(zf-zn)     10         0         zn*zf/(zn-zf)  0



dx 采用的是行向量,对于一个矢量 v 变换相当于  v * T.  而不是 T * v
opengl 采用的是列向量, 对于矢量变化为 T * v

左手坐标系和右手坐标系平移、缩放矩阵是相同的。
对于旋转矩阵,输入角度时有变化:
从轴的负端向正端看,左手坐标系  逆时针为正方向。即逆时针延z旋转旋转30 度。输入正数30 到旋转矩阵.
而右手坐标系相反,逆时针为负方向。即逆时针延z旋转30度。输入-30 到旋转矩阵

用U0 U1 U2 表示变换矩阵, 对于矩阵 R(U0 | U1 | U2)
对于右手坐标 U0 = U1 X U2   U1 = U2 X U0 U2 = U0 X U1
对于左手坐标 U0 = U2 X U1   U1 = U0 X U2 U2 = U1 X U0

而有些系统如gamebryo 采用右手坐标系但是Gamebryo矩阵旋转方向和默认右手坐标系相反,gb 延旋转轴顺时针为正。好处换成左手不用修改矩阵了。悲伤

尽管有这些左右顺序问题。 但很多放在内存中矩阵结构是一样的。这是为了方便使用intel SSE加速指令。
dx 矩阵存放结构
struct {
        float        _11, _12, _13, _14;   
        float        _21, _22, _23, _24;
        float        _31, _32, _33, _34;
        float        _41, _42, _43, _44;
};

posted on 2010-10-08 16:14  阿蒙1024  阅读(1171)  评论(0编辑  收藏  举报

导航