关于矩阵的那些破事儿

来源:互联网 发布:激光对射接单片机 编辑:程序博客网 时间:2024/05/16 19:31

人总是会为自己找捷径,而往往捷径是要付出不一般的代价。

 

呵呵---说说矩阵的事:

通过坐标变换将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              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 采用右手坐标系,这样虽然输入-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           00    2/h  0           00    0    1/(zf-zn)   00    0    zn/(zn-zf)  1
D3DXMatrixOrthoOffCenterLH:根据宽高和远近平面计算离心(0,0在窗口左上角)正交投影矩阵(左手)
r,l,b,t分别为右,左,下,上的坐标
2/(r-l)      0            0           00            2/(t-b)      0           00            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           00            2/(t-b)      0           00            0            1/(zn-zf)   0(l+r)/(l-r)  (t+b)/(b-t)  zn/(zn-zf)  1
 
D3DXMatrixOrthoRH:根据宽高和远近平面计算(0,0在窗口中心)正交投影矩阵(右手)
2/w  0    0           00    2/h  0           00    0    1/(zn-zf)   00    0    zn/(zn-zf)  1
 
D3DXMatrixPerspectiveFovLH:左手透视投影(参数为纵横张角和远近平面)
xScale     0          0               00        yScale       0               00          0       zf/(zf-zn)         10          0       -zn*zf/(zf-zn)     0where:yScale = cot(fovY/2)xScale = aspect ratio * yScale
D3DXMatrixPerspectiveFovRH:右手透视投影(参数为纵横张角和远近平面)
xScale     0          0              00        yScale       0              00        0        zf/(zn-zf)        -10        0        zn*zf/(zn-zf)      0where: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              00       2*zn/h  0              00       0       zf/(zf-zn)     10       0       zn*zf/(zn-zf)  0
 
D3DXMatrixPerspectiveOffCenterLH
2*zn/(r-l)   0            0              00            2*zn*(t-b)   0              0(l+r)/(l-r)  (t+b)/(b-t)  zf/(zf-zn)     10            0            zn*zf/(zn-zf)  0D3
 
DXMatrixPerspectiveOffCenterRH
2*zn/(r-l)   0            0                00            2*zn/(t-b)   0                0(l+r)/(r-l)  (t+b)/(t-b)  zf/(zn-zf)      -10            0            zn*zf/(zn-zf)    0
 
D3DXMatrixPerspectiveRH:
2*zn/w  0       0              00       2*zn/h  0              00       0       zf/(zn-zf)    -10       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中也使用同样的矩阵就可以了。
 
 
原创粉丝点击