实时阴影绘制技术研究

C++博客 首页 新随笔 联系 聚合 管理
  48 Posts :: 20 Stories :: 57 Comments :: 0 Trackbacks

   在看了《无极》,看了《逃出克隆岛》,看了《情癫大圣》以后,又把《friends》看到了season7,在感觉到一个人的节日极其无聊之后连续打了两天篮球,打到肌肉拉伤、疲惫,浑身像散了架子一样,然后没日没夜的睡觉用来休息,就这样,堕落的元旦假期过去了。

   在内心的极度悔恨自责之后,又投入到我的directX学习中来。昨天看完了坐标变换的部分,今天要在迎接保密检查的同时,把例子程序跑一跑,进一步巩固一下学习成果。
   上次写blog的时候只是简单记录了一下D3D的坐标变换,以及让做好的矩阵应用到场景中的顶点上,并探索了一下使用视口的方法。这次我就把D3D坐标变换其余的部分补充完整。首先介绍一种物体的表示法,然后介绍两种任意变换的方法,介绍视变换和投影变换,最后介绍深度缓冲的使用。
 
   上次介绍了对物体在三个坐标轴方向上做平移、缩放、旋转的情况,可以使用D3DX系列的API直接构造变换矩阵,那么再复杂一点的情况就是绕自身轴的旋转。使用上述基本变换实际上可以达到这一目的,只是有些麻烦,下面我们来探讨一下更为通用的表达方式。为了达到这一目的就必须利用更加复杂的矩阵变换。
   首先我们借助一个结构来描述物体的定位:
struts Object
{
   D3DMATRIX matLocal;
}
用该矩阵中的三个向量分别表示物体的朝向:Look,Up,Right,这个里的含义如同openGL里边相机的Look,Up,Right是一样的。然而实际上定位一个物体除了上述三个向量表示的姿态以外,还需要一个位置信息,于是我们用第四行来记录位置。将该矩阵设置为单位阵表示物体的变换从原点开始,沿坐标轴方向。这样表示以后,物体绕Look轴转就是横滚(pitch),绕Up轴转就是偏航(yaw),绕Right轴转就是俯仰(roll)。
------------------------------------
   下面说一下用到的API。将一个向量按照指定矩阵变换的API是:D3DXVectorTransformCoord(D3DXVector* vNew,D3DXVector* vOld,D3DXMatrix* mat),那么旋转用的矩阵mat又来源于D3DMatrixRotationAxis(D3DXMatrix* mat,D3DXVector* vAxis,FLOAT fRad),表示绕某一向量旋转一个角度产生一个变换矩阵。有了这两个API我们就可以通过将三个姿态向量指定给vAxis和vOld来获取到新的姿态向量。
   需要注意的是,由于计算精度问题,上述计算进行多次以后会存在舍入误差,使三个姿态向量不再垂直。为了解决这个问题需要在旋转之前对三个向量进行归一化。这里的归一化不是对三个向量各自归一,而是按照下述方式进行:
D3DXVec3Normalize(&vLook,&vLook);
D3DXVec3Cross(&vRight,&vUp,&vLook);
D3DXVec3Normalize(&vRight,&vRight);
D3DXVec3Cross(&vUp,&vLook,&vRight);
D3DXVec3Normalize(&vUp,&vUp);
可以看到是通过向量单独归一和叉乘的方式进行,既保证向量归一,又保证垂直。
   matLocal矩阵的保存形式如下:第一行是Right,第二行是Up,第三行是Look,第四行是Position:
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;
   下面我们把上述变换过程总结一下:
  1. 确定旋转角度和旋转轴。
  2. 取出当前的vRight,vLook,vUp,vPos向量;
  3. 对三个向量进行归一化;
  4. 利用D3DMatrixRotationAxis(D3DXMatrix* mat,D3DXVector* vAxis,FLOAT fRad)产生旋转矩阵;
  5. 利用D3DXVectorTransformCoord(D3DXVector* vNew,D3DXVector* vOld,D3DXMatrix* mat)对当前的vRight,vLook,vUp向量进行变换,得到新的vRight,vLook,vUp向量。
  6. 移动位置,获得新的vPos;
  7. 将新的vRight,vLook,vUp,vPos向量设置到matLocal中。

----------------------------

上边的表示方法我们看到要7个过程,这略微有些复杂,那么下面我们来看另外一种简洁的计算方法-四元数(Quaternion)。

  我们先对比一下实现的差别,然后再具体解释API的含义。

  1.  确定旋转角度和旋转轴。
  2. 利用D3DXQuaternionRotationYawPitchRoll(D3DXMatrix* mat,Float fYaw,FLOAT fPitch,FLOAT fRoll)的到变换矩阵。
  3. 把上述得到的矩阵同matLocal相乘得到新的matLocal;
  4. 做位置的变换。

   四元数的原理有点复杂,由于速成关系我也没有怎么看,只是知道可以简单想象成一个向量加上一次旋转,具体的运算推导有机会再研究吧。但这个东西用途的确很广泛,因此被作为一种专门的方法被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)来设置投影矩阵即可。

-------------------------------
   视口的使用上次已经说过了,下面就看一下深度缓冲的使用。
   在框架里边使用深度缓冲只要让m_d3dEnumeration.AppUsesDepthBuffer = TRUE.然后在每一帧绘制前用m_pd3Device->Clear()方法清空缓冲区。
   HRESULT Clear(
                 DWORD Count,//矩形数量
                 const D3DRECT *pRects,//矩形指针
                 DWORD Flags,//要清除的缓冲类型
                 float Z,//Z缓冲设置的值
                 DWORD Stencil)//模板缓冲设置的值
   书中还讲了一个深度缓冲精度影响渲染质量的问题,为了达到无错误的稳定效果可以是用W缓冲器。方法如下:
   m_pd3Device->SetRenderState(D3DTS_ZNABLE,D3DZB_USEW).
但是这需要硬件的支持,为了稳妥起见还是使用Z缓冲比较好。
-------------------------------
  总结来说,D3D要设置的矩阵分为三种:除了上次提到的世界坐标矩阵,其实我理解也就是openGL里边对应的模型视图矩阵中的模型矩阵,还有视矩阵和投影矩阵。它们的设置函数都是pDeviceObject->SetTransform(),只不过参数不同而已。所有的其他函数也好,表示也好最终都是为了获得这三个矩阵,这让我们拨开云雾见太阳,只要心中挂念这矩阵就可以了。
posted on 2006-01-05 11:48 苦行僧 阅读(854) 评论(0)  编辑 收藏 引用 所属分类: directX

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理