3D游戏从入门到精通Word格式文档下载.docx
《3D游戏从入门到精通Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《3D游戏从入门到精通Word格式文档下载.docx(31页珍藏版)》请在冰豆网上搜索。
比如赋值,相加,相减等等。
它的定义如下:
typedefstructD3DXVECTOR3:
publicD3DVECTOR{
public:
D3DXVECTOR3(){};
D3DXVECTOR3(CONSTFLOAT*);
D3DXVECTOR3(CONSTD3DVECTOR&
);
D3DXVECTOR3(CONSTD3DXFLOAT16*);
D3DXVECTOR3(FLOATx,FLOATy,FLOATz);
//casting
operatorFLOAT*();
operatorCONSTFLOAT*()const;
//assignmentoperators
D3DXVECTOR3&
operator+=(CONSTD3DXVECTOR3&
D3DXVECTOR3&
operator-=(CONSTD3DXVECTOR3&
operator*=(FLOAT);
operator/=(FLOAT);
//unaryoperators
D3DXVECTOR3operator+()const;
D3DXVECTOR3operator-()const;
//binaryoperators
D3DXVECTOR3operator+(CONSTD3DXVECTOR3&
)const;
D3DXVECTOR3operator-(CONSTD3DXVECTOR3&
D3DXVECTOR3operator*(FLOAT)const;
D3DXVECTOR3operator/(FLOAT)const;
friendD3DXVECTOR3operator*(FLOAT,CONSTstructD3DXVECTOR3&
BOOLoperator==(CONSTD3DXVECTOR3&
BOOLoperator!
=(CONSTD3DXVECTOR3&
}D3DXVECTOR3,*LPD3DXVECTOR3;
在这里已经实现向量的加减、数乘、数除,也许你发现这里有没有点积和叉积运算。
它们是通过其它公共函数实现的。
说到这里,如果你对点积和叉积运算不是很懂,看来又需要去看看《向量代数》了。
在游戏里,要表示一个人沿着OA的方向行走,那么只需要对着OA向量数乘,就可以了。
有了这样的向量类型定义,就可像下面那样方便使用:
D3DXVECTOR3vOA(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使用Direct3D
2.11.1最简单三角形显示
前面已经学习了顶点、平面以及平面的法向量。
接着下面就带你进入奇幻的3D世界,由你去创造这个美好的3D世界。
任何美好的事物都是简单的东西组成的,所以也需要从最简单的物体构造开始。
这里主要显示一个三角形,其它关于窗口和初始化D3D的设备都已经由MM3D引擎做好了,目前只要设置顶点、世界坐标变换矩阵、观察变换矩阵、投影变换矩阵,然后渲染这个三角形,就可以显示屏幕里了。
最终显示效果如下图:
1、顶点格式
顶点格式是可变的,是灵活的。
由前面可知,顶点肯定是有三个值的,就是三个坐标轴的值。
下面先来看看最简单的顶点格式,它定义如下:
//定义顶点缓冲区里用到的顶点格式。
structVT_CAITRIANGLE
{
D3DXVECTOR3vPosition;
//顶点位置。
staticconstDWORDdwFVF=D3DFVF_XYZ;
};
这里使用一个结构来定义顶点的格式,后面就可使用这个结构来定义顶点了。
看到这个结构里第一个成员vPosition是很熟悉的吧,前面已经说明过使用一个向量来表示一个顶点的。
最简单的顶点格式,就是只有一个向量值,其它什么也没有。
后面一个成员dwFVF是说明顶点的有什么样数据,这里只有顶点的坐标,所使用了宏定义D3DFVF_XYZ。
其实顶点还可以有其它数据在里面的,比如要表示这个顶点的法向量,就需要加入法向量的成员,比如要显示纹理在这个表面上,就需要设置纹理坐标。
1、顶点缓冲区
顶点的数据是怎么样保存在内存里的呢,由于渲染需要,就要把顶点保存在内存缓冲区里。
最简单的
方法就是使用D3D提供的顶点缓冲区,即是使用IDirect3DVertexBuffer9接口的顶点缓冲区管理,它
是通过D3D的设备来创建。
这个接口实现顶点的资源管理,使用起来非常方便,由于它管理内存分配,
这样就省了内存管理。
下面就是通过IDirect3DDevice9:
:
CreateVertexBuffer函数来创建一个顶点缓冲区:
HRESULThr;
//创建顶点缓冲区。
if(FAILED(hr=m_pd3dDevice->
CreateVertexBuffer(3*2*sizeof(VT_CAITRIANGLE),
0,VT_CAITRIANGLE:
dwFVF,
D3DPOOL_MANAGED,&
m_pVB,NULL)))
//创建顶点缓冲区失败。
returnDXTRACE_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))){
//锁住顶点缓冲区。
Lock"
}
//前面三角形。
pVertices[0].vPosition=D3DXVECTOR3(-1.0f,-1.0f,0.0f);
pVertices[1].vPosition=D3DXVECTOR3(0.0f,1.0f,0.0f);
pVertices[2].vPosition=D3DXVECTOR3(1.0f,-1.0f,0.0f);
//后面三角形。
pVertices[3].vPosition=D3DXVECTOR3(-1.0f,-1.0f,0.0f);
pVertices[4].vPosition=D3DXVECTOR3(1.0f,-1.0f,0.0f);
pVertices[5].vPosition=D3DXVECTOR3(0.0f,1.0f,0.0f);
//解锁顶点缓冲区。
m_pVB->
Unlock();
这段代码是通过IDirect3DVertexBuffer9:
Lock方法来获取到顶点缓冲区的指针,然后把6个顶点赋值。
最后调用IDirect3DVertexBuffer9:
Unlock方法解锁,如果不解锁,D3D显示不了这个缓冲区里的数据,
就会出错的,所以一定要小心地操作。
1、世界坐标变换矩阵
在设置顶点缓冲区之后,就需要设置物体的世界坐标变换矩阵。
从解析几何里可以知道,无论二维还
是三维的图像都可以通过齐次坐标来变换的。
目前在三维里的变换,采用的矩阵是4×
4的矩阵来实现
变换处理。
在D3D里定义D3DXMATRIX类型,使用它来表达一个矩阵,并且定义一系列的函数可以实现矩阵的运算。
typedefstruct_D3DMATRIX{
union{
struct{
float_11,_12,_13,_14;
float_21,_22,_23,_24;
float_31,_32,_33,_34;
float_41,_42,_43,_44;
};
floatm[4][4];
}D3DMATRIX;
上面代码定义了4×
4矩阵的四个元素,全部使用浮点数运算。
接着还添加了一些操作方法,如下:
typedefstructD3DXMATRIX:
publicD3DMATRIX{
D3DXMATRIX(){};
D3DXMATRIX(CONSTFLOAT*);
D3DXMATRIX(CONSTD3DMATRIX&
D3DXMATRIX(CONSTD3DXFLOAT16*);
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);
//accessgrants
FLOAT&
operator()(UINTRow,UINTCol);
FLOAToperator()(UINTRow,UINTCol)const;
//castingoperators
D3DXMATRIX&
operator*=(CONSTD3DXMATRIX&
D3DXMATRIX&
operator+=(CONSTD3DXMATRIX&
operator-=(CONSTD3DXMATRIX&
D3DXMATRIXoperator+()const;
D3DXMATRIXoperator-()const;
D3DXMATRIXoperator*(CONSTD3DXMATRIX&
D3DXMATRIXoperator+(CONSTD3DXMATRIX&
D3DXMATRIXoperator-(CONSTD3DXMATRIX&
D3DXMATRIXoperator*(FLOAT)const;
D3DXMATRIXoperator/(FLOAT)const;
friendD3DXMATRIXoperator*(FLOAT,CONSTD3DXMATRIX&
BOOLoperator==(CONSTD3DXMATRIX&
=(CONSTD3DXMATRIX&
}D3DXMATRIX,*LPD3DXMATRIX;
上面的方法,都是对这个矩阵的运算,使用起来更加方便。
有了矩阵的定义,还有了矩阵的运算,那么构造一个从模型坐标到世界坐标变换的矩阵,就是很容易的事情了。
由于我不需要变换它,直接使用一个单位矩阵,就可以了。
由单位矩阵可知,任何数据乘以单位矩阵都不变会改变。
代码如下:
//设置世界坐标矩阵。
D3DXMATRIXmatIdentity;
D3DXMatrixIdentity(&
matIdentity);
m_pd3dDevice->
SetTransform(D3DTS_WORLD,&
这段代码里先定义一个矩阵,然后调用函数D3DXMatrixIdentity初始化这个矩阵为单位矩阵,最后通过函数SetTransform设置为世界坐标变换矩阵。
这样显示的三角形就是按模型坐标系显示到世界坐标系里,也就是在世界坐标系的原点位置显示。
在游戏里,所有3D模型都是在外面做好的,因此都是模型坐标系,也就是局部坐标系,要把它们显示到世界坐标系里,一般是需要通过变换的。
因为所有模型坐标系都是相同的,都是原点在世界坐标系的原点。
如果不通过变换就显示的话,所有的物体都是重叠在原点位置,这样就分不清楚它们的显示。
比如显示一个足球场,两个球门就是分别显示的。
一个球门在原点,一个球门就在离原点一段距离再显示,还需要通过合适旋转变换才能显示。
由于两个球门是同一个局部坐标系的物体构成,必须通过世界坐标变换矩阵才能显示出来。
1、视图变换矩阵
视图变换矩阵是做什么用的呢,为什么需要视图变换矩阵呢,其实这个问题很好回答和理解的。
在3D世界里,所有东西都是跟现实中的实物一样的,当用人的眼睛去观看现实中的实物时,总是从某一方向去观察的,这样就看到实物组成的图像。
就像你拿着照相去照相是一样的,首先要调好焦距,对准物体。
在D3D里也需要调好焦距,对准物体,否则什么也看不到。
因此在D3D里,就需要设置好视图变换矩阵。
也就是相机的位置和焦距。
在视图变换矩阵里,是使用两个点来表达相机位置和焦距,
在D3D里使用向量来表示相机位置,如下:
D3DXVECTOR3vFromPt=D3DXVECTOR3(0.0f,0.0f,-5.0f);
这里指明了相机所在位置是Z轴上-5的位置。
接着又需要指明焦点所有位置,如下:
D3DXVECTOR3vLookatPt=D3DXVECTOR3(0.0f,0.0f,0.0f);
这里指明了相机焦点所在位置是在世界坐标系的原点。
但还有一个因素没有考虑进去,那就是相机放置的方向。
比如人站在地球上,头顶着天去观察实物是
一种方向,是一种正常的方向,侧着头去看实物又是一种方向,人倒立着看实物又是一个方向。
“横看
成岭侧成峰,远近高低各不同。
”就表达了这种不同方向看东西的不同,因此在D3D里也需要设置方
向,用如下向量表示:
//设置向上方向为Y轴正方向.
D3DXVECTOR3vUp(0,1,0);
有了这三个向量,就决定了相机需要经过的变换矩阵,也就是看到D3D里的物体是什么样子了。
在
D3D里,就是构造一个视图变换矩阵,它是使用下面函数来计算的:
D3DXMatrixLookAtLH(&
m_mView,&
m_vEyePosition,&
m_vLookAtPosition,&
vUp);
第一个参数,就是返回来的视图变换矩阵,第二个参数是相机位置,第三个参数是焦点位置,第四个
参数是相机朝向。
构造好视图变换矩阵后,就可以把它设置D3D里,通过下面的函数来实现:
//设置视变换矩阵。
constD3DXMATRIX&
mView=CCameraFirstPerson:
Instance()->
GetViewMatrix();
SetTransform(D3DTS_VIEW,&
mView);
这样就把D3D的设备视图变换矩阵设置好了。
投影变换矩阵
投影变换矩阵是做什么的呢,这个更容易理解了。
大家都应看过照片吧,那大家又想过照片是怎么样表现原来的物体呢,看到远处的山是小的,近处的人是大的。
并且只有在一定的范围内的物体才照得清楚,其它的就不见了。
在D3D里就需要把三维的物体空间投影到二维的屏幕空间显示,这就需要投影了。
本来人眼看现实的物体时,总是经过投影变换的,把三维的物体通通变换成二维东西,这样就在人眼上看见东西就是三维的了。
现在这个投影变换就是模拟人眼的作用,沿着光线的方向,那些能看见,那些不能看见。
那些是近的,那些是远的。
通过这样的计算,就会生成二维的图像,跟人眼看到真实的世界是相同的。
如下图所示:
在D3D里通过设置投影前截面、后截面、视角和长宽比来构造投影矩阵,使用下面的函数来构造这个投影变换矩阵:
D3DXMatrixPerspectiveFovLH(&
m_mProjection,fFOV,fAspect,fNearPlane,fFarPlane);
第一个参数是返回的矩阵,第二参数是视角,第三个参数是长宽比,第四参数是近截面,第五参数是远截面。
构造好投影变换矩阵,就可以通过下面参数设置:
//设置投影矩阵。
mProj=CCameraFirstPerson:
GetProjectionMatrix();
SetTransform(D3DTS_PROJECTION,&
mProj);
这样就可以设置好D3D的设备投影矩阵
1、渲染物体
经过前面这么多步骤,做足了准备工作,就可以渲染物体,显示三维空间的图像了。
这时心情要兴奋
起来,就看到自己辛苦的果实了。
仔细地看一下前面的代码,创建的缓冲区还没有设置给D3D设备。
下面就来做这方面的工作:
//渲染顶点缓冲区的内容。
SetStreamSource(0,m_pVB,0,sizeof(VT_CAITRIANGLE));
SetFVF(VT_CAITRIANGLE:
dwFVF);
DrawPrimitive(D3DPT_TRIANGLELIST,0,2);
上面这段代码先设置顶点缓冲区给D3D设备,第一个参数选择渲染通道,由于现在的显示卡提供多个
通道显示,以便加速显示。
这个参数可以根据枚举设备的特性来知道最大的通道数。
第二个参数是上面的顶点缓冲区,就是每个顶点的坐标值。
第三个参数是指明从顶点缓冲区什么位置开始渲染,这里是从缓冲区开始位置开始,所以设置为0。
第四个参数是每个顶点占用的大小,因为每个顶点的数据,可以有三个坐标值、纹理坐标、颜色值和
其它数据。
接着下来调用SetFVF函数来设置顶点的格式,这里