这些天对左边变换比较感兴趣。申明,本人对游戏编程没兴趣,只是近体做的项目用了DirectX而已,用了就要懂么!况且坐标变换也挺用脑的,我的大脑已经很久没用了,快锈死了,得动动了。
m._11 m._12 m._13 m._14 ux uy uz 0
m._21 m._22 m._23 m._24 = vx vy vz 0
m._31 m._32 m._33 m._34 wx wy wz 0
m._41 m._42 m._43 m._44 tx ty tz 1
下面看一下D3D需要构造的几个基本矩阵:
1,平移矩阵
1 0 0 0
0 1 0 0
0 0 1 0
tx ty tz 1
平移矩阵可以由函数:VOID D3DMatrixTranslation(D3DMATRIX* m,FLOAT tx,FLOAT ty,FLOAT tz)来构造。
2,旋转矩阵
以绕Y轴旋转为例,矩阵形式如下:
cosR 0 -sinR 0
0 1 0 0
sinR 0 cosR 0
0 0 0 1
以上矩阵可以通过类似VOID D3DMatrixRotationY(D3DMATRIX* m,FLOAT r)来获得。同样可以得到绕X,Z轴旋转的矩阵。
3,缩放矩阵
sx 0 0 0
0 sy 0 0
0 0 sz 0
0 0 0 1
可以通过D3DMATRIX* D3DMatrixScaling(D3DMATRIX *pOut,FLOAT sx,FLOAT sy,FLOAT sz)来获得。
在C++中由于利用函数重载,还可以写成:pOut = pM1 * pM2的形式,当然也可以连乘:pOut = pM1 * pM2 * pM3.
获得了变换矩阵以后,要对顶点起作用,需要利用m_pDevice->SetTransform(D3DTS_WORLD,&m_matWorld)来设置。当然可以将D3DTS_WORLD换成D3DTS_VIEW,D3DTS_PROJECTION来设置视矩阵和投影矩阵。
HRESULT CMyD3DApplication::FrameMove()
{
// rotates the object about the y-axis
D3DXMATRIX matRotY;
D3DXMATRIX matRotZ;
D3DXMatrixRotationY( &matRotY, m_fTime * 0.5f);
D3DXMatrixRotationZ( &matRotZ, m_fTime * 1.5f);
D3DXMATRIX matTrans;
D3DXMatrixTranslation(&matTrans, 0.0f, 0.0f, 0.0f);
}
再看一小段代码:
// Each viewport fills a quarter of the window
m_RViewport.Width = MainViewport.Width / 2;
m_RViewport.Height = MainViewport.Height / 2;
m_RViewport.Y = m_SRTViewport.Y = 0;
m_RViewport.X = m_TRViewport.X = 0;
// Set the full Z range for each viewport
m_RViewport.MinZ = 0.0f;
m_RViewport.MaxZ = 1.0f;
m_pd3dDevice->SetViewport(&m_RViewport);
m_matWorld = RotationMatrix1;
m_pd3dDevice->SetTransform(D3DTS_WORLD, &m_matWorld);
m_pd3dDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST,
0,
0,
4, // number of vertices
0,
2); // number of primitives
我们只要把已经做好的矩阵设置成当前世界坐标矩阵,然后依次绘制图元就可以了。
从这个例子我们也可以想象出d3d也存在着类似于openGL那样的一个保存当前状态的自动机。
上边的代码中我们也看到了使用视口的例子,通过设置不同视口的位置和大小,我们可以在屏幕上显示出不同的内容,书中就是使用四个视口分别展示了四种不同的变换。
上次写blog的时候只是简单记录了一下D3D的坐标变换,以及让做好的矩阵应用到场景中的顶点上,并探索了一下使用视口的方法。这次我就把D3D坐标变换其余的部分补充完整。首先介绍一种物体的表示法,然后介绍两种任意变换的方法,介绍视变换和投影变换,最后介绍深度缓冲的使用。
struts Object{D3DMATRIX matLocal;}
D3DXVec3Normalize(&vLook,&vLook);D3DXVec3Cross(&vRight,&vUp,&vLook);D3DXVec3Normalize(&vRight,&vRight);D3DXVec3Cross(&vUp,&vLook,&vRight);D3DXVec3Normalize(&vUp,&vUp);
m_pObjects[0].matLocal._11 = vRight.x;m_pObjects[0].matLocal._12 = vRight.y;m_pObjects[0].matLocal._13 = vRight.z;m_pObjects[0].matLocal._21 = vUp.x;m_pObjects[0].matLocal._22 = vUp.y;m_pObjects[0].matLocal._23 = vUp.z;m_pObjects[0].matLocal._31 = vLook.x;m_pObjects[0].matLocal._32 = vLook.y;m_pObjects[0].matLocal._33 = vLook.z;m_pObjects[0].matLocal._41 = vPos.x;m_pObjects[0].matLocal._42 = vPos.y;m_pObjects[0].matLocal._43 = vPos.z;
- 确定旋转角度和旋转轴。
- 取出当前的vRight,vLook,vUp,vPos向量;
- 对三个向量进行归一化;
- 利用D3DMatrixRotationAxis(D3DXMatrix* mat,D3DXVector* vAxis,FLOAT fRad)产生旋转矩阵;
- 利用D3DXVectorTransformCoord(D3DXVector* vNew,D3DXVector* vOld,D3DXMatrix* mat)对当前的vRight,vLook,vUp向量进行变换,得到新的vRight,vLook,vUp向量。
- 移动位置,获得新的vPos;
- 将新的vRight,vLook,vUp,vPos向量设置到matLocal中。
----------------------------
上边的表示方法我们看到要7个过程,这略微有些复杂,那么下面我们来看另外一种简洁的计算方法-四元数(Quaternion)。
我们先对比一下实现的差别,然后再具体解释API的含义。
- 确定旋转角度和旋转轴。
- 利用D3DXQuaternionRotationYawPitchRoll(D3DXMatrix* mat,Float fYaw,FLOAT fPitch,FLOAT fRoll)的到变换矩阵。
- 把上述得到的矩阵同matLocal相乘得到新的matLocal;
- 做位置的变换。
四元数的原理有点复杂,由于速成关系我也没有怎么看,只是知道可以简单想象成一个向量加上一次旋转,具体的运算推导有机会再研究吧。但这个东西用途的确很广泛,因此被作为一种专门的方法被D3D介绍。
上边只用到了一个API,那就是D3DXQuaternionRotationYawPitchRoll(D3DXMatrix* mat,Float fYaw,FLOAT fPitch,FLOAT fRoll),给定绕三个轴的旋转角度,返回一个变换矩阵。
-----------------------------
下边看一下观察变换,观察矩阵同物体定位矩阵唯一不同的就是其存储方式,它采取列向量的存储方式。相机的各种变换同物体的变换没有任何不同,最后也是得到一个矩阵,只是D3D提供了一个根据视点位置,相机朝向和向上方向构造矩阵的函数:D3DXMatrixLookAtLH(D3DXMatrix* mat,D3DXVECTOR3* pEye,D3DXVECTOR3* pAt,D3DXVECTOR3* pUp),省着自己算了。最后用m_pd3Device->SetTransform(D3DTS_VIEW,&mat)设置一下就可以了。
这里需要注意的是D3DXMatrixLookAtLH()只适合于简单的头罩式显示或者视点跟随,对于具有复杂旋转的飞行模拟器这类相机最好还是自己来算。计算的方式同前边介绍的物体变换的方式一样,也有两种方式,一种是复杂的7步变换,一种是简单的四元数变换。最后将得到的向量按照列向量的形式赋给视矩阵,再利用SetTransform()设置一下就好了。这实际上是一种自己维护相机的方式。
------------------------------
下面看一下投影变换。提到投影就会想到视锥,就会有视域角(FOV-field of view)、宽高比(aspect)和远近裁减面这四个参数。在D3D里边可以利用D3DXMatrixPerspectiveFoVLH(D3DXMATRIX* pOut,FLOAT fovY,FLOAT Aspect,FLOAT zn,FLOAT zf),通过给定的四个参数获得投影矩阵,然后用m_pd3Device->SetTranform(D3DTS_PROJECTION,&pOut)来设置投影矩阵即可。