3D游戏程序设计入门(DirectX®9.0)(更新:04-10)翁云兵声明:本教程内容绝大部分来自FrankD.Luna所著的《Introductionto3DGameProgrammingwithDirectX9.0》。教程内容(特别是语言表达上)大部分是我根据自己理解所写的,因此也不是此书的中文翻译版。由于我的英文水平很差,翻译过来就难免有错了,望读者原谅。当然如你认为我的水平实在是让人无法忍受那么请直接查阅英文教程。由于我的工作太忙且水平有限,计划一周一篇文章。希望读者能够支持我。给我多提意见。此中文教程版权归我所有。非商业应用可免费使用本教程。商业应用请同作者联系,Email:WengYB@126.com。特别感谢:是他让我走上了游戏开发的道路。一直关心支持我的同事、同学。我最最亲爱的老婆,没有她我不可能写出这本教程。第一部分必备的数学知识在这最开始的一部分中我们将介绍本书所要用到的数学知识。我们讨论的主题是向量,矩阵和相应的变换,当然还有一些有关面和线的内容。最开始阅读时这部分是可选的。本教程对这些知识的讨论是很有限的,因此对于不同数学知识背景的读者来说都容易阅读。对于想了解更多更全的这方面信息的读者,请查看有关线性代数的书籍。当然已经学习过线性代数的读者也可将它作为有必要的复习内容来阅读。(这里推荐你看看《线性代数与空间解析几何》)除此之外,我们还将展示D3DX类中相关的数学模型和执行特殊变换的函数。目标学习向量以及它们的3D计算机图形程序学习矩阵以及学会使用它们来变换3D图形学习怎样模拟面和线以及它们的3D图形程序熟悉用于3D数学运算的D3DX库中包含的类和程序的子集三维空间中的向量几何学中,我们用有向线段表示向量,如图1。向量的两个属性是他的长度和他的顶点所指的方向。因此,可以用向量来模拟既有大小又有方向的物理模型。例如,以后我们要实现的粒子系统。我们用向量来模拟粒子的速度和加速度。在3D计算机图形学中我们用向量不仅仅模拟方向。例如我们常常想知道光线的照射方向,以及在3D世界中的摄象机。向量为在3维空间中表示方向的提供了方便。图1向量与位置无关。有同样长度和方向的两个向量是相等的,即使他们在不同的位置。观察彼此平行的两个向量,例如在图1中u和v是相等的。我们继续学习左手坐标系。图2显示的是左手坐标系和右手坐标系。两者不同的是Z轴的方向。在左手坐标系Z轴是向书的里面跑的而右手坐标系是向书的外边跑的。图2因为向量的位置不能改变它的性质,我们能把所有向量平移使他们的尾部和坐标系的原点重合。因此,当一个向量在标准位置我们能通过头点来描述向量。图3显示的是图1中的向量在标准位置的样子。图3我们通常用小写字母表示一个向量,但有时也用大写字母。如2、3和4维向量分别是:u=(ux,uy),N=(Nx,Ny,Nz),c=(cx,cy,cz,cw)。我们现在介绍3D中的4个向量,就象图4显示的。首先是都由含有0的零向量;它被表示成加粗的0=(0,0,0)。接下来3个特殊的向量标准基向量。它们被叫做i,j和k向量,分别沿着坐标系的x轴,y轴和z轴,并且有1的单位长:i=(1,0,0),j=(0,1,0),andk=(0,0,1)。注意:只有1个单位长度的向量叫做单位向量。图4在D3DX库中,我们能用D3DXVECTOR3类表示3维空间中的向量。它的定义是:typedefstructD3DXVECTOR3:publicD3DVECTOR{public:D3DXVECTOR3(){};D3DXVECTOR3(CONSTFLOAT*);D3DXVECTOR3(CONSTD3DVECTOR&);D3DXVECTOR3(CONSTD3DXFLOAT16*);D3DXVECTOR3(FLOATx,FLOATy,FLOATz);//castingoperatorFLOAT*();operatorCONSTFLOAT*()const;//assignmentoperatorsD3DXVECTOR3&operator+=(CONSTD3DXVECTOR3&);D3DXVECTOR3&operator-=(CONSTD3DXVECTOR3&);D3DXVECTOR3&operator*=(FLOAT);D3DXVECTOR3&operator/=(FLOAT);//unaryoperatorsD3DXVECTOR3operator+()const;D3DXVECTOR3operator-()const;//binaryoperatorsD3DXVECTOR3operator+(CONSTD3DXVECTOR3&)const;D3DXVECTOR3operator-(CONSTD3DXVECTOR3&)const;D3DXVECTOR3operator*(FLOAT)const;D3DXVECTOR3operator/(FLOAT)const;friendD3DXVECTOR3operator*(FLOAT,CONSTstructD3DXVECTOR3&);BOOLoperator==(CONSTD3DXVECTOR3&)const;BOOLoperator!=(CONSTD3DXVECTOR3&)const;}D3DXVECTOR3,*LPD3DXVECTOR3;注意D3DXVECTOR3是从D3DVECTOR继承的。它的定义是:typedefstruct_D3DVECTOR{floatx,y,z;}D3DVECTOR;向量有它们自己的算法,就象你在D3DXVECTOR3定义中看到的数学运算。现在你不需要知道它们怎么使用。以后介绍这些向量运算以及一些有用的函数和关于向量它们重要的详细资料。注意:在3D图形程序中,虽然我们主要关心3D向量,但有时也会用到2D和4D向量。在D3DX库中提供了D3DXVECTOR2和D3DXVECTOR4类来分别表现2D和4D向量。不同维数的向量有着和3D向量一样的性质,也就是它们描述大小和方向,仅仅是在不同的维数中。所有这些向量的数学运算对于不同维数向量都有效只是有一个除外,就是向量积。这些运算我们可通过论述3D向量扩展到2D,4D甚至n维向量。向量相等几何学上,有同样方向和长度的两个向量相等。数学上,我们说有同样维数和分量的向量相等。例如:如果ux=vx,uy=vy,且uz=vz.那么(ux,uy,uz)=(vx,vy,vz)。在代码中我们能够用“==”判断两个向量相等。D3DXVECTORu(1.0f,0.0f,1.0f);D3DXVECTORv(0.0f,1.0f,0.0f);if(u==v)returntrue;同样的,我们也能用“!=”判断两个向量不相等。if(u!=v)returntrue;注意:当比较浮点数时,必须注意。因为浮点数不是精确的,我们认为相等的两个浮点数是有细微差别的;因此,我们测试它们近似相等。我们定义一个常数EPSILON,把它当作非常小的“buffer”。假如两个数和EPSILON相差很小我们说它们近似相等。换句话说,EPSILON让浮点数有一定的精度。接下来的实例函数是怎样用EPSILON比较两个浮点数相等。boolEquals(floatlhs,floatrhs){//iflhs==rhstheirdifferenceshouldbezeroreturnfabs(lhs-rhs)EPSILON?true:false;}当我们用D3DXVECTOR3类时不必担心,因为它已经帮我们处理了,但是在一般情况下适当注意比较两个浮点数是很重要的。计算向量大小几何学上,向量的大小是有向线段的长度。知道向量的分量,利用下面的公式我们就能计算出向量的大小。‖u‖表示向量u的长度。例如:计算向量u=(1,2,3)和v=(1,1)的大小。根据公式(1),我们得到:我们利用D3DX库中下面的函数便能计算向量的大小。FLOATD3DXVec3Length(//Returnsthemagnitude.CONSTD3DXVECTOR3*pV//Thevectortocomputethelengthof.);D3DXVECTOR3v(1.0f,2.0f,3.0f);floatmagnitude=D3DXVec3Length(&v);//=sqrt(14)规范化向量规范化向量是让向量的大小等于1,即被叫作单位向量。我们能利用向量大小以及各个分量把一个向量规范化,就象这样:我们这样表示单位向量û。如:规范化向量u=(1,2,3)和v=(1,1)。解答方法:根据(2)和(3)我们得到‖u‖=√14和‖v‖=√2,因此:我们利用D3DX库中下面的函数能规范化向量。D3DXVECTOR3*D3DXVec3Normalize(D3DXVECTOR3*pOut,//Result.CONSTD3DXVECTOR3*pV//Thevectortonormalize.);注意:这个函数返回一个指针因此它能作为一个参数传递给另一个函数。在极大程度上,除非非常特殊的,D3DX数学函数返回一个指针。我们不要轻易的说所有的函数都是这样。向量相加我们能够通过分别把两个向量的各个分量相加得到向量之和,注意在相加之前必须保证它们有相同的维数。图5显示的是几何学上的向量相加。图5我们用重载加法操作符把两个向量相加的代码:D3DXVECTOR3u(2.0f,0.0f,1.0f);D3DXVECTOR3v(0.0f,-1.0f,5.0f);//(2.0+0.0,0.0+(-1.0),1.0+5.0)D3DXVECTOR3sum=u+v;//=(2.0f,-1.0f,6.0f)向量相减和加法类似,通过分别把两个向量的各个分量相减得到向量之差。再次重声两个向量必须是相同维数。图6显示的是几何学上的向量相减。图6我们用重载减法操作符把两个向量相减的代码:D3DXVECTOR3u(2.0f,0.0f,1.0f);D3DXVECTOR3v(0.0f,-1.0f,5.0f);D3DXVECTOR3difference=u-v;//=(2.0f,1.0f,-4.0f)图6显示,向量减法得到一个从v向量终点到u向量终点的向量。假如我们解释u和v的分量,我们能用向量相减找到从一个点到另一个点的向量。这是非常方便的操作因为我们常常想找到从一个点到另一个点的方向的向量。数与向量的乘积我们能用一个数与向量相乘,就象名字暗示的一样,向量按比例变化。这种运算不会改变向量的方向,除非我们用负数去操作,这样也只是方向相反了。D3DXVECTOR3类提供了一个这种操作的操作符。D3DXVECTOR3u(1.0f,1.0f,-1.0f);D3DXVECTOR3scaledVec=u*10.0f;//=(10.0f,10.0f,-10.0f)点积数学上定义点积是两个向量的乘积。按下面等式计算:上面的等式不能很明显的体现几何上的意义。利用余弦定律,我们能够发现它们的关系。u·v=|u||v|cosθ,表示两个向量的点积是它们的摸和夹角的余弦之积。因此,如果u和v都是单位向量,那么u·v就是它们夹角的余弦。一些点积有用的特性■假如u·v=0,那么u⊥v。■假如u·v0,那么两个向量的角度θ小于90度。■假如u·v0,那么两个向量的角度θ大于90度。我们使用下面的D3DX函数计算两个向量的点积:FLOATD3DXVec3Dot(//Returnstheresult.CONSTD3DXVECTOR3*pV1,//Leftsidedoperand.CONSTD3DXVECTOR3*pV2//Rightsidedoperand.);D3DXVECTOR3u(1.0f,-1.0f,0.0f);D3DXVECTOR3v(3.0f,2.0f,1.0f);//1.0*3.0+-1.0*2.0+0.0*1.0//=3.0+-2.0floatdot=D3DXVec3Dot(&u,&v);//=1.0叉积第二类乘法是向量的叉积。不象点积,用一个数来