关于矩阵

Posted by Vincent on May 4, 2015

转的啊

人总是会为自己找捷径,而往往捷径是要付出不一般的代价。 呵呵—说说矩阵的事: 通过坐标变换将3D空间的图元转换成2D图元的过程:主要为世界变换->视图变换->投影变换->视口变换 3D场景中的任何物体,都是由一个一个三角形组成的。而三角形位置信息的就是其各个顶点的三维坐标。这是用来在模型中存储的,而要把物体显示在屏幕上,还需要将它们转换成显示器上的二维坐标。这就需要对每个点实施一套 3 to 2 的转换公式,在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() 不要被这个函数吓倒。其实观察矩阵作用和世界矩阵差不多,只是起到旋转、平移的作用。而且是把世界坐标系的物体映射到观察坐标系。只要用摄像机世界变换的逆就好了 V = T*Rz*Ry*Rz 。 实际上可以先获得摄像机的世界坐标 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]的立方体更好一些)分为透视投影和正交投影。正交投影不会随物体远近影响物体投影的大小。而透视则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 0
0 2*zn/h 0 0
0 0 zf/(zf-zn) 1
0 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 0
0 2*zn/t-b 0 0
r+l/r-l t+b/t-b zf/(zf-zn) 1
0 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 采用右手坐标系,这样虽然输入-30度,再经过转置成列向量矩阵,经过2次变换和dx的旋转矩阵结果反倒是相同的。但 dx 是 向量 v * T . 而 gamebryo 由于列向量 是 T * V. 不能被相同的值所迷惑. 实际上是Gamebryo旋转方向和右手坐标系相反,我日。要自己输入符号。好处换成左手不用修改矩阵了

dx 矩阵存放结构

struct {
        float        _11, _12, _13, _14;    
        float        _21, _22, _23, _24;
        float        _31, _32, _33, _34;
        float        _41, _42, _43, _44; 
};

DX里面一些好用的矩阵变换函数也记录下: D3DXMatrixLookAtLH:根据摄像机的三个属性坐标计算视矩阵(左手)

zaxis = normal(At - Eye)
xaxis = normal(cross(Up, zaxis))
yaxis = cross(zaxis, xaxis)
xaxis.x yaxis.x zaxis.x 0
xaxis.y yaxis.y zaxis.y 0
xaxis.z yaxis.z zaxis.z 0
-dot(xaxis, eye) -dot(yaxis, eye) -dot(zaxis, eye) 1 D3DXMatrixLookAtRH :根据摄像机的三个属性坐标计算
视矩阵(右手)zaxis = normal(Eye - At)
xaxis = normal(cross(Up, zaxis))
yaxis = cross(zaxis, xaxis)

xaxis.x yaxis.x zaxis.x 0
xaxis.y yaxis.y zaxis.y 0
xaxis.z yaxis.z zaxis.z 0
-dot(xaxis, eye) -dot(yaxis, eye) -dot(zaxis, eye) 1

D3DXMatrixOrthoLH:根据宽高和远近平面计算(0,0在窗口中心)正交投影矩阵(左手)

2/w 0 0 0
0 2/h 0 0
0 0 1/(zf-zn) 0
0 0 zn/(zn-zf) 1

D3DXMatrixOrthoOffCenterLH:根据宽高和远近平面计算离心(0,0在窗口左上角)正交投影矩阵(左手) r,l,b,t分别为右,左,下,上的坐标

2/(r-l) 0 0 0
0 2/(t-b) 0 0
0 0 1/(zf-zn) 0
(l+r)/(l-r) (t+b)/(b-t) zn/(zn-zf) 1

D3DXMatrixOrthoOffCenterRH:根据宽高和远近平面计算(0,0在窗口中心)正交投影矩阵(右手)

2/(r-l) 0 0 0
0 2/(t-b) 0 0
0 0 1/(zn-zf) 0
(l+r)/(l-r) (t+b)/(b-t) zn/(zn-zf) 1

D3DXMatrixOrthoRH:根据宽高和远近平面计算(0,0在窗口中心)正交投影矩阵(右手)

2/w 0 0 0
0 2/h 0 0
0 0 1/(zn-zf) 0
0 0 zn/(zn-zf) 1

D3DXMatrixPerspectiveFovLH:左手透视投影(参数为纵横张角和远近平面)

xScale 0 0 0
0 yScale 0 0
0 0 zf/(zf-zn) 1
0 0 -zn*zf/(zf-zn) 0
where:
yScale = cot(fovY/2)

xScale = aspect ratio * yScale

D3DXMatrixPerspectiveFovRH:右手透视投影(参数为纵横张角和远近平面)

xScale 0 0 0
0 yScale 0 0
0 0 zf/(zn-zf) -1
0 0 zn*zf/(zn-zf) 0
where:
yScale = cot(fovY/2)

xScale = aspect ratio * yScale

D3DXMatrixPerspectiveLH:实境体投影矩阵

(D3DXMatrixPerspectiveLH(&matProj,D3DX_PI/4,1.0f,1.0f,100.0f);)
2*zn/w 0 0 0
0 2*zn/h 0 0
0 0 zf/(zf-zn) 1
0 0 zn*zf/(zn-zf) 0
 
D3DXMatrixPerspectiveOffCenterLH:
2*zn/(r-l) 0 0 0
0 2*zn*(t-b) 0 0
(l+r)/(l-r) (t+b)/(b-t) zf/(zf-zn) 1
0 0 zn*zf/(zn-zf) 0D3
 
DXMatrixPerspectiveOffCenterRH
2*zn/(r-l) 0 0 0
0 2*zn/(t-b) 0 0
(l+r)/(r-l) (t+b)/(t-b) zf/(zn-zf) -1
0 0 zn*zf/(zn-zf) 0

 
D3DXMatrixPerspectiveRH:
2*zn/w 0 0 0
0 2*zn/h 0 0
0 0 zf/(zn-zf) -1
0 0 zn*zf/(zn-zf) 0

D3DXMatrixReflect:根据平面方程计算反射矩阵 P = normalize(Plane);

-2 * P.a * P.a + 1 -2 * P.b * P.a -2 * P.c * P.a 0
-2 * P.a * P.b -2 * P.b * P.b + 1 -2 * P.c * P.b 0
-2 * P.a * P.c -2 * P.b * P.c -2 * P.c * P.c + 1 0
-2 * P.a * P.d -2 * P.b * P.d -2 * P.c * P.d 1

有这些矩阵的计算方式,在opengl中也使用同样的矩阵就可以了