3D游戏从入门到精通.doc_第1页
3D游戏从入门到精通.doc_第2页
3D游戏从入门到精通.doc_第3页
3D游戏从入门到精通.doc_第4页
3D游戏从入门到精通.doc_第5页
已阅读5页,还剩22页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

2.10.1坐标系在D3D里使用的坐标系是左手坐标系,就是当你看屏幕时,左手边是X轴的负方向,右手边是X轴的正方向。屏幕下边是Y轴的负方向,屏幕上边是Y轴的正方向。远离屏幕是Z轴的负方向,指向屏幕里面是Z轴的正方向。坐标系还有右手坐标系,还有其它各种各样的坐标系。如果需要从其它坐标里的图形显示在D3D里,就需要进行转换。2.10.2点在三维空间里表示物体位置,就需要用坐标来表示。由上面的坐标系可知,任何一点,都可以用三个坐标轴的值来表示。比如表示零点,就是O(0,0,0)。只要给出三个坐标轴的值,就可以知道这个点在什么位置了。从坐标系里,就知道我们定义的坐标轴是有方向的。那么在坐标系里的点,是否也有方向呢?为了表示方便,使用起来简单,在D3D里的点也是有方向的。用向量来表一个点的位置和方向。一个点的方向,都是从原点出发指向这点的方向。比如下面的图中,OA就是一个点的位置和方向。从几何上知道这种表示方法了,但在程序里是怎么样表示一个向量呢?在编程里,是需要特定的编程语言来表达向量的。由于C具有强大的功能和特别快的处理速度,目前大部份游戏都是采用C来编写。因此D3D游戏也不例外地采用C。如果你还不会使用C,建议你先去找本C的书来看看,然后再接着看本书,否则很多东西是看不懂的。在C里是使用结构(struct)或者类(class)来表达一个向量的。下面就是D3D里的向量表示方法:typedef struct _D3DVECTOR float x;float y;float z; D3DVECTOR;D3DVECTOR向量定义了三个成员x,y,z来表示坐标系三个坐标轴的值。这里使用typedef来定义向量类型,以后就可以使用它来定义其它向量类型变量了。为了方便对它的操作,因此又需要继承它,生成一个新类D3DXVECTOR3。D3DXVECTOR3类有很多方法,使用起来非常方便。因而在程序里,主要使用它来定义变量,这样就有很函数可以使用了。比如赋值,相加,相减等等。它的定义如下:typedef struct D3DXVECTOR3 : public D3DVECTORpublic:D3DXVECTOR3() ;D3DXVECTOR3( CONST FLOAT * );D3DXVECTOR3( CONST D3DVECTOR& );D3DXVECTOR3( CONST D3DXFLOAT16 * );D3DXVECTOR3( FLOAT x, FLOAT y, FLOAT z );/ castingoperator FLOAT* ();operator CONST FLOAT* () const;/ assignment operatorsD3DXVECTOR3& operator += ( CONST D3DXVECTOR3& );D3DXVECTOR3& operator -= ( CONST D3DXVECTOR3& );D3DXVECTOR3& operator *= ( FLOAT );D3DXVECTOR3& operator /= ( FLOAT );/ unary operatorsD3DXVECTOR3 operator + () const;D3DXVECTOR3 operator - () const;/ binary operatorsD3DXVECTOR3 operator + ( CONST D3DXVECTOR3& ) const;D3DXVECTOR3 operator - ( CONST D3DXVECTOR3& ) const;D3DXVECTOR3 operator * ( FLOAT ) const;D3DXVECTOR3 operator / ( FLOAT ) const;friend D3DXVECTOR3 operator * ( FLOAT, CONST struct D3DXVECTOR3& );BOOL operator = ( CONST D3DXVECTOR3& ) const;BOOL operator != ( CONST D3DXVECTOR3& ) const; D3DXVECTOR3, *LPD3DXVECTOR3;在这里已经实现向量的加减、数乘、数除,也许你发现这里有没有点积和叉积运算。它们是通过其它公共函数实现的。说到这里,如果你对点积和叉积运算不是很懂,看来又需要去看看向量代数了。在游戏里,要表示一个人沿着OA的方向行走,那么只需要对着OA向量数乘,就可以了。有了这样的向量类型定义,就可像下面那样方便使用:D3DXVECTOR3 vOA(1.0f,1.0f,1.0f);2.10.1平面的表示3D空间里的物体都是实体的,主要由平面组成。比如正方体,就有6个平面。在D3D里组成三维物体的基本平面是三角形,无论是多么复杂的物体,都是有三角形组成的。并且D3D里支持显示最小的图形也是三角形,它是没有点和直线显示的。因此,要显示正方体,就需要把每个平面分成两个三角形,才能显示。如下图所示:为什么选择三角形作为D3D里最小的平面呢?由于三个点组成的平面,肯定是共面的,是平坦的,并且显示效率是最高的。如果选择其它多边形的话,就存在点不共面的情况,这样变得非常复杂,所以目前显示卡也是针对三角形进行优化显示的。要在D3D里显示物体,就是显示很多的三角形,让这些三角形连接起来,就组成游戏里的人体,动物,武器等等。在几何里就知道每个三角形都有三个顶点组成,那么只要设置了三个顶点的值,就可以在D3D里显示三角形出来了。用D3D里的向量表示三个顶点就是:A: D3DXVECTOR3( -1.0f, -1.0f, 0.0f );B: D3DXVECTOR3( 0.0f, 1.0f, 0.0f );C: D3DXVECTOR3( 1.0f, -1.0f, 0.0f );只要把所有三角形的顶点,都按上面格式设置到顶点缓冲区里,就可以显示一个三角形出来。但这个三角形又可分为两个表面:前表面和后表面。在D3D缺省情况下,只会显示前表面。那么怎么样来区别前表面和后表面呢?由于D3D是采用左手坐标系,正对着平面看去是顺时针方向的顶点顺序就是前表面。如下图所示:如果不小心把顶点的顺序搞反,就会看不到表面显示。并且还会关系到后面的灯光显示,纹理显示。其实,前表面就是平面的法向量指向的表面。只要认识到组成平面的顶点以及顶点排列顺序,就可以用平面构成三维物体了。不管多么复杂的物体,都可以由顶点组成平面,再有平面组成物体。不管是圆的物体,还是方的物体,都可以使用三角形去组成。其实这种三维物体是数字化的,永远都是尽可能逼近,不可能是完全相同的。比如要显示一个足球,它是圆的,需要非常多的三角形去构成它,但不可能完全一样的。并且使用过多的三角形,就会导致CPU或者GPU处理不过来。在目前的个人电脑里,虽然处理三角形的速度快了很多,但真的要显示大量三角形,还是不行的。21 使用Direct3D2.11.1最简单三角形显示前面已经学习了顶点、平面以及平面的法向量。接着下面就带你进入奇幻的3D世界,由你去创造这个美好的3D世界。任何美好的事物都是简单的东西组成的,所以也需要从最简单的物体构造开始。这里主要显示一个三角形,其它关于窗口和初始化D3D的设备都已经由MM3D引擎做好了,目前只要设置顶点、世界坐标变换矩阵、观察变换矩阵、投影变换矩阵,然后渲染这个三角形,就可以显示屏幕里了。最终显示效果如下图:1、 顶点格式顶点格式是可变的,是灵活的。由前面可知,顶点肯定是有三个值的,就是三个坐标轴的值。下面先来看看最简单的顶点格式,它定义如下:/定义顶点缓冲区里用到的顶点格式。struct VT_CAITRIANGLED3DXVECTOR3 vPosition; / 顶点位置。static const DWORD dwFVF = D3DFVF_XYZ;这里使用一个结构来定义顶点的格式,后面就可使用这个结构来定义顶点了。看到这个结构里第一个成员vPosition是很熟悉的吧,前面已经说明过使用一个向量来表示一个顶点的。最简单的顶点格式,就是只有一个向量值,其它什么也没有。后面一个成员dwFVF是说明顶点的有什么样数据,这里只有顶点的坐标,所使用了宏定义D3DFVF_XYZ。其实顶点还可以有其它数据在里面的,比如要表示这个顶点的法向量,就需要加入法向量的成员,比如要显示纹理在这个表面上,就需要设置纹理坐标。1、 顶点缓冲区顶点的数据是怎么样保存在内存里的呢?由于渲染需要,就要把顶点保存在内存缓冲区里。最简单的方法就是使用D3D提供的顶点缓冲区,即是使用IDirect3DVertexBuffer9接口的顶点缓冲区管理,它是通过D3D的设备来创建。这个接口实现顶点的资源管理,使用起来非常方便,由于它管理内存分配,这样就省了内存管理。下面就是通过IDirect3DDevice9:CreateVertexBuffer函数来创建一个顶点缓冲区:HRESULT hr;/ 创建顶点缓冲区。if( FAILED( hr = m_pd3dDevice-CreateVertexBuffer( 3*2*sizeof(VT_CAITRIANGLE),0, VT_CAITRIANGLE:dwFVF,D3DPOOL_MANAGED, &m_pVB, NULL ) ) )/创建顶点缓冲区失败。return DXTRACE_ERR( CreateVertexBuffer, hr );上面这段代码,就是创建6个顶点的缓冲区。指定的格式为VT_CAITRIANGLE:dwFVF,这个在前面已经定义过,只包括顶点向量数据。D3DPOOL_MANAGED指明了创建的缓冲区在系统内存里,并且需要时就拷贝到显示卡内存里。我们知道显示卡内存越来越大了,如果把很多变化不大的物体放到显示卡内存里,就可以加速显示,减少占用系统局部总线的带宽。像NV新出来的显示卡,都有1G显示内存,显然可以放更多物体在那里,以便显示的速度更快。由于显示卡内存和系统内存的复杂性,而通过这个接口可以方便地使用了。由于它是还一个内存池,所以产生内存碎片的机会就更加少了。创建顶点缓冲区之后,就需要往里面填充顶点数据,先来看看下面的代码:/ 用两个三角形填充顶点缓冲区。VT_CAITRIANGLE* pVertices;if( FAILED( hr = m_pVB-Lock( 0, 0, (VOID*)&pVertices, 0 ) ) )/锁住顶点缓冲区。return DXTRACE_ERR( Lock, hr );/ 前面三角形。pVertices0.vPosition = D3DXVECTOR3( -1.0f, -1.0f, 0.0f );pVertices1.vPosition = D3DXVECTOR3( 0.0f, 1.0f, 0.0f );pVertices2.vPosition = D3DXVECTOR3( 1.0f, -1.0f, 0.0f );/ 后面三角形。pVertices3.vPosition = D3DXVECTOR3( -1.0f, -1.0f, 0.0f );pVertices4.vPosition = D3DXVECTOR3( 1.0f, -1.0f, 0.0f );pVertices5.vPosition = D3DXVECTOR3( 0.0f, 1.0f, 0.0f );/解锁顶点缓冲区。m_pVB-Unlock();这段代码是通过IDirect3DVertexBuffer9:Lock方法来获取到顶点缓冲区的指针,然后把6个顶点赋值。最后调用IDirect3DVertexBuffer9:Unlock方法解锁,如果不解锁,D3D显示不了这个缓冲区里的数据,就会出错的,所以一定要小心地操作。1、 世界坐标变换矩阵在设置顶点缓冲区之后,就需要设置物体的世界坐标变换矩阵。从解析几何里可以知道,无论二维还是三维的图像都可以通过齐次坐标来变换的。目前在三维里的变换,采用的矩阵是44的矩阵来实现变换处理。在D3D里定义D3DXMATRIX类型,使用它来表达一个矩阵,并且定义一系列的函数可以实现矩阵的运算。它的定义如下:typedef struct _D3DMATRIX union struct float _11, _12, _13, _14;float _21, _22, _23, _24;float _31, _32, _33, _34;float _41, _42, _43, _44;float m44; D3DMATRIX;上面代码定义了44矩阵的四个元素,全部使用浮点数运算。接着还添加了一些操作方法,如下:typedef struct D3DXMATRIX : public D3DMATRIXpublic:D3DXMATRIX() ;D3DXMATRIX( CONST FLOAT * );D3DXMATRIX( CONST D3DMATRIX& );D3DXMATRIX( CONST D3DXFLOAT16 * );D3DXMATRIX( FLOAT _11, FLOAT _12, FLOAT _13, FLOAT _14,FLOAT _21, FLOAT _22, FLOAT _23, FLOAT _24,FLOAT _31, FLOAT _32, FLOAT _33, FLOAT _34,FLOAT _41, FLOAT _42, FLOAT _43, FLOAT _44 );/ access grantsFLOAT& operator () ( UINT Row, UINT Col );FLOAT operator () ( UINT Row, UINT Col ) const;/ casting operatorsoperator FLOAT* ();operator CONST FLOAT* () const;/ assignment operatorsD3DXMATRIX& operator *= ( CONST D3DXMATRIX& );D3DXMATRIX& operator += ( CONST D3DXMATRIX& );D3DXMATRIX& operator -= ( CONST D3DXMATRIX& );D3DXMATRIX& operator *= ( FLOAT );D3DXMATRIX& operator /= ( FLOAT );/ unary operatorsD3DXMATRIX operator + () const;D3DXMATRIX operator - () const;/ binary operatorsD3DXMATRIX operator * ( CONST D3DXMATRIX& ) const;D3DXMATRIX operator + ( CONST D3DXMATRIX& ) const;D3DXMATRIX operator - ( CONST D3DXMATRIX& ) const;D3DXMATRIX operator * ( FLOAT ) const;D3DXMATRIX operator / ( FLOAT ) const;friend D3DXMATRIX operator * ( FLOAT, CONST D3DXMATRIX& );BOOL operator = ( CONST D3DXMATRIX& ) const;BOOL operator != ( CONST D3DXMATRIX& ) const; D3DXMATRIX, *LPD3DXMATRIX;上面的方法,都是对这个矩阵的运算,使用起来更加方便。有了矩阵的定义,还有了矩阵的运算,那么构造一个从模型坐标到世界坐标变换的矩阵,就是很容易的事情了。由于我不需要变换它,直接使用一个单位矩阵,就可以了。由单位矩阵可知,任何数据乘以单位矩阵都不变会改变。代码如下:/ 设置世界坐标矩阵。D3DXMATRIX matIdentity;D3DXMatrixIdentity( &matIdentity );m_pd3dDevice-SetTransform( D3DTS_WORLD, &matIdentity );这段代码里先定义一个矩阵,然后调用函数D3DXMatrixIdentity初始化这个矩阵为单位矩阵,最后通过函数SetTransform设置为世界坐标变换矩阵。这样显示的三角形就是按模型坐标系显示到世界坐标系里,也就是在世界坐标系的原点位置显示。在游戏里,所有3D模型都是在外面做好的,因此都是模型坐标系,也就是局部坐标系,要把它们显示到世界坐标系里,一般是需要通过变换的。因为所有模型坐标系都是相同的,都是原点在世界坐标系的原点。如果不通过变换就显示的话,所有的物体都是重叠在原点位置,这样就分不清楚它们的显示。比如显示一个足球场,两个球门就是分别显示的。一个球门在原点,一个球门就在离原点一段距离再显示,还需要通过合适旋转变换才能显示。由于两个球门是同一个局部坐标系的物体构成,必须通过世界坐标变换矩阵才能显示出来。1、 视图变换矩阵视图变换矩阵是做什么用的呢?为什么需要视图变换矩阵呢?其实这个问题很好回答和理解的。在3D世界里,所有东西都是跟现实中的实物一样的,当用人的眼睛去观看现实中的实物时,总是从某一方向去观察的,这样就看到实物组成的图像。就像你拿着照相去照相是一样的,首先要调好焦距,对准物体。在D3D里也需要调好焦距,对准物体,否则什么也看不到。因此在D3D里,就需要设置好视图变换矩阵。也就是相机的位置和焦距。在视图变换矩阵里,是使用两个点来表达相机位置和焦距,如下图所示:在D3D里使用向量来表示相机位置,如下:D3DXVECTOR3 vFromPt = D3DXVECTOR3( 0.0f, 0.0f, -5.0f );这里指明了相机所在位置是Z轴上-5的位置。接着又需要指明焦点所有位置,如下:D3DXVECTOR3 vLookatPt = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );这里指明了相机焦点所在位置是在世界坐标系的原点。但还有一个因素没有考虑进去,那就是相机放置的方向。比如人站在地球上,头顶着天去观察实物是一种方向,是一种正常的方向,侧着头去看实物又是一种方向,人倒立着看实物又是一个方向。“横看成岭侧成峰,远近高低各不同。”就表达了这种不同方向看东西的不同,因此在D3D里也需要设置方向,用如下向量表示:/设置向上方向为Y轴正方向.D3DXVECTOR3 vUp(0,1,0);有了这三个向量,就决定了相机需要经过的变换矩阵,也就是看到D3D里的物体是什么样子了。在D3D里,就是构造一个视图变换矩阵,它是使用下面函数来计算的:D3DXMatrixLookAtLH( &m_mView, &m_vEyePosition, &m_vLookAtPosition, &vUp );第一个参数,就是返回来的视图变换矩阵,第二个参数是相机位置,第三个参数是焦点位置,第四个参数是相机朝向。构造好视图变换矩阵后,就可以把它设置D3D里,通过下面的函数来实现:/设置视变换矩阵。const D3DXMATRIX& mView = CCameraFirstPerson:Instance()-GetViewMatrix();m_pd3dDevice-SetTransform( D3DTS_VIEW, &mView);这样就把D3D的设备视图变换矩阵设置好了。投影变换矩阵投影变换矩阵是做什么的呢?这个更容易理解了。大家都应看过照片吧,那大家又想过照片是怎么样表现原来的物体呢?看到远处的山是小的,近处的人是大的。并且只有在一定的范围内的物体才照得清楚,其它的就不见了。在D3D里就需要把三维的物体空间投影到二维的屏幕空间显示,这就需要投影了。本来人眼看现实的物体时,总是经过投影变换的,把三维的物体通通变换成二维东西,这样就在人眼上看见东西就是三维的了。现在这个投影变换就是模拟人眼的作用,沿着光线的方向,那些能看见,那些不能看见。那些是近的,那些是远的。通过这样的计算,就会生成二维的图像,跟人眼看到真实的世界是相同的。如下图所示:在D3D里通过设置投影前截面、后截面、视角和长宽比来构造投影矩阵,使用下面的函数来构造这个投影变换矩阵:D3DXMatrixPerspectiveFovLH( &m_mProjection, fFOV, fAspect, fNearPlane, fFarPlane );第一个参数是返回的矩阵,第二参数是视角,第三个参数是长宽比,第四参数是近截面,第五参数是远截面。构造好投影变换矩阵,就可以通过下面参数设置:/ 设置投影矩阵。const D3DXMATRIX& mProj = CCameraFirstPerson:Instance()-GetProjectionMatrix();m_pd3dDevice-SetTransform( D3DTS_PROJECTION, &mProj );这样就可以设置好D3D的设备投影矩阵1、 渲染物体经过前面这么多步骤,做足了准备工作,就可以渲染物体,显示三维空间的图像了。这时心情要兴奋起来,就看到自己辛苦的果实了。仔细地看一下前面的代码,创建的缓冲区还没有设置给D3D设备。下面就来做这方面的工作:/ 渲染顶点缓冲区的内容。m_pd3dDevice-SetStreamSource( 0, m_pVB, 0, sizeof(VT_CAITRIANGLE) );m_pd3dDevice-SetFVF( VT_CAITRIANGLE:dwFVF );m_pd3dDevice-DrawPrimitive( D3DPT_TRIANGLELIST, 0, 2 );上面这段代码先设置顶点缓冲区给D3D设备,第一个参数选择渲染通道,由于现在的显示卡提供多个通道显示,以便加速显示。这个参数可以根据枚举设备的特性来知道最大的通道数。第二个参数是上面的顶点缓冲区,就是每个顶点的坐标值。第三个参数是指明从顶点缓冲区什么位置开始渲染,这里是从缓冲区开始位置开始,所以设置为0。第四个参数是每个顶点占用的大小,因为每个顶点的数据,可以有三个坐标值、纹理坐标、颜色值和其它数据。接着下来调用SetFVF函数来设置顶点的格式,这里设置为上面的定义的顶点格式VT_CAITRIANGLE:dwFVF。最后一行代码就是真正地画两个三角形在屏幕缓冲区里。它的第一个参数是设置显示顶点的格式,这里按三角形列表的形式来显示。第二个参数是设置从那个顶点开始,这里是从0顶点开始。第三个参数是表示有多少个三角形,这里有两个三角形。通过上面的过程来学习D3D是怎么样渲染一帧图像的,不管有多么复杂的物体要显示,也不管有多少个三角形要显示,所有的过程是一样的。开始看到的三角形就显示出来了,也许你发现这里只显示三角形的直线,其实我设置了D3D的设备只显示线框模型。如下面所示:/只显示线框.m_pd3dDevice-SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);使用这种模式,可以很方便调试没有设置光线的模型。也很容易看出来有多少个三角形在那里显示。这个例子非常简单吧,其实3D的图像显示就是这么样简单的,不过要理解它的意思,还是看一下我的例子代码和调试一下,就更会快速度掌握D3D了。2.10.1索引缓冲区上面已经学习了最简单的三角形显示,了解了整个3D物体的显示流程,下面来学习一个复杂一点的物体显示,使用12个三角形构造成的正方体显示。并且通过个例子学会使用索引缓冲区,提高图形绘制的速度和效率。1、 使用三角形构造立方体由前面可知,任何复杂的物体,都是由三角形组成的。现在就用12三角形构造一个正方体。先创建12点个顶点的缓冲区,然后再往里填写合适的数据,然后显示它出来。下面来仔细分析这段代码:在函数HRESULT CCAICube:Init(IDirect3DDevice9* pd3dDevice)里,先创建12顶点数据。const int nVTCount = 6*2*3;/ 创建顶点缓冲区。if( FAILED( hr = m_pd3dDevice-CreateVertexBuffer( nVTCount * sizeof(VT_CAICUBE),0, VT_CAICUBE:dwFVF,D3DPOOL_MANAGED, &m_pVB, NULL ) ) )/创建顶点缓冲区失败。return DXTRACE_ERR( CreateVertexBuffer, hr );这段代码,跟以前的代码一样创建顶点缓冲区。接着下来就指明所有三角形的坐标:/ 用三角形填充顶点缓冲区。VT_CAICUBE* pVertices;if( FAILED( hr = m_pVB-Lock( 0, 0, (VOID*)&pVertices, 0 ) ) )/锁住顶点缓冲区。return DXTRACE_ERR( Lock, hr );/ 三角形组成的立方体表面。/第一个面pVertices0.vPosition = D3DXVECTOR3( -1.0f, -1.0f, -1.0f );pVertices1.vPosition = D3DXVECTOR3( -1.0f, 1.0f, -1.0f );pVertices2.vPosition = D3DXVECTOR3( 1.0f, 1.0f, -1.0f );/pVertices3.vPosition = D3DXVECTOR3( -1.0f, -1.0f, -1.0f );pVertices4.vPosition = D3DXVECTOR3( 1.0f, 1.0f, -1.0f );pVertices5.vPosition = D3DXVECTOR3( 1.0f, -1.0f, -1.0f );/第二个面pVertices6.vPosition = D3DXVECTOR3( -1.0f, 1.0f, -1.0f );pVertices7.vPosition = D3DXVECTOR3( -1.0f, 1.0f, 1.0f );pVertices8.vPosition = D3DXVECTOR3( 1.0f, 1.0f, -1.0f );/pVertices9.vPosition = D3DXVECTOR3( -1.0f, 1.0f, 1.0f );pVertices10.vPosition = D3DXVECTOR3( 1.0f, 1.0f, 1.0f );pVertices11.vPosition = D3DXVECTOR3( 1.0f, 1.0f, -1.0f );/第三个面pVertices12.vPosition = D3DXVECTOR3( -1.0f, 1.0f, 1.0f );pVertices13.vPosition = D3DXVECTOR3( -1.0f, -1.0f, 1.0f );pVertices14.vPosition = D3DXVECTOR3( 1.0f, 1.0f, 1.0f );/pVertices15.vPosition = D3DXVECTOR3( 1.0f, 1.0f, 1.0f );pVertices16.vPosition = D3DXVECTOR3( -1.0f, -1.0f, 1.0f );pVertices17.vPosition = D3DXVECTOR3( 1.0f, -1.0f, 1.0f );/第四个面pVertices18.vPosition = D3DXVECTOR3( -1.0f, -1.0f, 1.0f );pVertices19.vPosition = D3DXVECTOR3( 1.0f, -1.0f, -1.0f );pVertices20.vPosition = D3DXVECTOR3( 1.0f, -1.0f, 1.0f );/pVertices21.vPosition = D3DXVECTOR3( -1.0f, -1.0f, 1.0f );pVertices22.vPosition = D3DXVECTOR3( -1.0f, -1.0f, -1.0f );pVertices23.vPosition = D3DXVECTOR3( 1.0f, -1.0f, -1.0f );/第五个面pVertices24.vPosition = D3DXVECTOR3( 1.0f, 1.0f, 1.0f );pVertices25.vPosition = D3DXVECTOR3( 1.0f, -1.0f, -1.0f );pVertices26.vPosition = D3DXVECTOR3( 1.0f, 1.0f, -1.0f );/pVertices27.vPosition = D3DXVECTOR3( 1.0f, -1.0f, -1.0f );pVertices28.vPosition = D3DXVECTOR3( 1.0f, 1.0f, 1.0f );pVertices29.vPosition = D3DXVECTOR3( 1.0f, -1.0f, 1.0f );/第六个面pVertices30.vPosition = D3DXVECTOR3( -1.0f, 1.0f, -1.0f );pVertices31.vPosition = D3DXVECTOR3( -1.0f, -1.0f, -1.0f );pVertices32.vPosition = D3DXVECTOR3( -1.0f, 1.0f, 1.0f );/pVertices33.vPosition = D3DXVECTOR3( -1.0f, 1.0f, 1.0f );pVertices34.vPosition = D3DXVECTOR3( -1.0f, -1.0f, -1.0f );pVertices35.vPosition = D3DXVECTOR3( -1.0f, -1.0f, 1.0f );/解锁顶点缓冲区。m_pVB-Unlock();上面从第一个面开始,就每个面两个三角形地填写,并且是按左中手坐标系的方向来填写三角形的顶点。接着修改渲染的代码,改为渲染12个三角形,代码如下:/ 渲染顶点缓冲区的内容。m_pd3dDevice-SetStreamSource( 0, m_pVB, 0, sizeof(VT_CAICUBE) );m_pd3dDevice-SetFVF( VT_CAICUBE:dwFVF );m_pd3dDevice-DrawPrimitive( D3DPT_TRIANGLELIST, 0, 12 );这样显示出来的立方体是那个面都可以看得到的,如果不按左手坐标系填写,就会有一些面看不到的。通过上面的代码,是否看到要显示一个简单的立方体,就需要复杂地填写顶点了,并且要小心翼翼地写代码,一不小心写错了,就看不见了。因此,再复杂的物体,一般是通过使用3D建模软件来构造的。通过上面的代码,也发现了一个问题,就是整个正方体,其实只有8个顶点,而上面的三角形列表里有很多顶点是重复的,对于只有12个三角形来说,也许不是很重要。但是对于数以万计的三角形来说,这样的重复顶点就会浪费很多内存,浪费系统的带宽,并且需要重复渲染顶点。有没有更好的方法来解决这个问题呢?答案是肯定的,下面接着就来学习索引缓冲区。1、 使用索引缓冲区什么是索引缓冲区呢?其实索引缓冲区,就像指针一样的工具。顶点缓冲区里保存的是真实的顶点,而索引缓冲区只记录顶点缓冲区的顶点编号。比如顶点缓冲区里有4个顶点,而这4个顶点就可以构成两个三角形来显示。如果直接使用顶点缓冲区的话,就需要写6个顶点。这样就可以使用索引缓冲区,指明第一个三角形是由顶点编号1、2、3构成一个三角形,而第二个三角形就是1、3、4组成。现在把上面的立方体改为使用索引缓冲区,先创建8个顶点,如下:pVertices0.vPosition = D3DXVECTOR3( -1.0f, -1.0f, -1.0f );pVertices1.vPosition = D3DXVECTOR3( -1.0f, 1.0f, -1.0f );pVertices2.vPosition = D3DXVECTOR3( 1.0f, 1.0f, -1.0f );pVertices3.vPosition = D3DXVECTOR3( 1.0f, -1.0f, -1.0f );pVertices4.vPosition = D3DXVECTOR3( -1.0f, 1.0f, 1.0f );pVertices5.vPosition = D3DXVECTOR3( 1.0f, 1.0f, 1.0f );pVertices6.vPosition = D3DXVECTOR3( 1.0f, -1.0f, 1.0f );pVertices7.vPosition = D3DXVECTOR3( -1.0f, -1.0f, 1.0f );这样,就可省下许多顶点占用的内存了,然后创建36个索引点的缓冲区,如下:/创建索引缓冲区。hr = m_pd3dDevice-CreateIndexBuffer( 12*3 *sizeof(WORD),0, D3DFMT_INDEX16, D3DPOOL_DEFAULT,&m_pIB, NULL );if( FAILED( hr ) )return E_FAIL;上面创建了36个WORD大小的索引点,这样省了很多内存空间。由于顶点是由3个浮点数和其它值组成,肯定比两个字节索引值占用空间大。接着下来就是设置索引缓冲区,如下:void* pIndices;if( FAILED( m_pIB-Lock( 0, sizeof(WORD)*12*3, &pIndices,0 ) ) )m_pIB-Release();m_pIB = NULL;return E_FAIL;/WORD* pIndex = reinterpret_cast(pIndices);int nPos = 0;/1pIndexnPos+ = 0;pIndexnPos+ = 1;pIndexnPos+ = 2;pIndexnPos+ = 0;pIndexnPos+ = 2;pIndexnPos+ = 3;/2pIndexnPos+ = 1;pIndexnPos+ = 4;pIndexnPos+ = 2;pIndexnPos+ = 2;pIndexnPos+ = 4;pIndexnPos+ = 5;/3pIndexnPos+ = 2;pIndexnPos+ = 5;pIndexnPos+ = 3;pIndexnPos+ = 3;pIndexnPos+ = 5;pIndexnPos+ = 6;/4pIndexnPos+ = 0;pIndexnPos+ = 7;pIndexnPos+ = 1;pIndexnPos+ = 1;pIndexnPos+ = 7;pIndexnPos+ = 4;/5pIndexnPos+ = 0;pIndexnPos+ = 3;pIndexnPos+ = 7;pIndexnPos+ = 3;pIndexnPos+ = 6;pIndexnPos+ = 7;/6pIndexnPos+ = 4;pIndexnPos+ = 7;pIndexnPos+ = 5;pIndexnPos+ = 5;pIndexnPos+ = 7;pIndexnPos+ = 6;/m_pIB-Unlock();上面这段代码,先Lock函数取得索引缓冲区地址,然后依次设置索引值,这里使用三角形列表,所以顶点顺序一样是按顺时针方向来设置的,也就是按左手坐标系来设置的。接着下来,就需要渲染这个立方体了,如下:m_pd3dDevice-SetStreamSource( 0, m_pVB, 0, sizeof(VT_CAICUBE) );m_pd3dDevice-SetFVF( VT_CAICUBE:dwFVF );m_pd3dDevice-SetIndices(m_pIB);m_pd3dDevice-DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,0,8,0,12);上面代码先设置了8个顶点缓冲区到管道里,然后再调用SetIndices设置索引缓冲区,最后调用DrawIndexedPrimitive函数来显示所有三角形列表。DrawIndexedPrimitive函数的第一个参数是索引列表里的类型,第二个参数是索引缓冲区在顶点缓冲区里的起始位置,比如顶点缓区是0,1,3的,当设置这个值为50时,那么相当于访问了50,51,53的顶点值。第三个参数是最小索引值。第四个参数是有多少个顶点会使用。第五个参数是从那个索引值开始。第六个参数是基本图形个数。通过这样学习,就学会使用了索引缓冲区,如果有很多顶点,有很多三角形,就需要使用优化的算法,以便有最少的顶点,使用最多的索引,这样有利于减少内存占用,提高带宽利用,提高显示卡显示更多图形,提高游戏的性能。2.10.1基本图形显示在D3D里只有几种基本图形可以显示的,它们分别是:点列表、线列表、线带列表、三角形列表、三角形带列表、三角形扇形列表。其它任何的图形,都可以由这向这几种基本图形组合出来。上面已经介绍了三角形显示了,所以这里就不再具体地说明它了。1、 点列表点列表主要显示为一个个像素点的集合,每个点都是单独显示,分离的。点列表作用是用来显示点元素,或者显示点线的。如下图所示:上面这个图就是使用点列表来显示一条正弦曲线。其实它们都是由分理的点组成,每个点可显示为不同的颜色。现在就看看怎么样用代码显示这条正弦曲线的。HRESULT hr;/ 创建顶点缓冲区。if( FAILED( hr = m_pd3dDevice-CreateVertexBuffer(m_nPointListCount * sizeof(VT_CAIPRIMITIVE),0, VT_CAIPRIMITIVE:dwFVF,D3DPOOL_MANAGED, &m_pvbPointList, NULL ) ) )/创建顶点缓冲区失败。return DXTRACE_ERR( CreateVertexBuffer, hr );/VT_CAIPRIMITIVE* pVertices;if( FAILED( hr = m_pvbPointList-Lock( 0, 0, (VOID*)&pVertices, 0 ) ) )/锁住顶点缓冲区。return DXTRACE_ERR( Lock, hr );for (int i = 0; i Unlock();上面这段代码,先创建点列表的顶点缓冲区,然后使用正弦函数计算点坐标的值,并把它们设置到顶点缓冲区里。接着就需要调用渲染函数:m_pd3dDevice-SetStreamSource( 0, m_pvbPointList, 0, sizeof(VT_CAIPRIMITIVE) );m_pd3dDevice-SetFVF( VT_CAIPRIMITIVE:dwFVF );m_pd3dDevice-Dr

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论