D3D是一个十分繁琐的工具,尽管Microsoft已经尽可能地简化了,但是为了实现一个效果,你还是不得不做许多的工作,其中就包括对各种复杂的结构的赋值;这些赋值很重要,它直接关系到程序的最终效果。
这个部分,是我初学D3D时的一个总结。
首先,获取一个D3D设备:
LPDIRECT3DDEVICE9 pd3dDevice;
LPDIRECT3D9 pD3D = Direct3DCreate9( D3D_SDK_VERSION );
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof( d3dpp ) );
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &pd3dDevice );
pd3dDevice 就是我们需要的D3D设备。这个设备是为一个窗口应用程序写的,如果你是想要一个全屏的D3D程序的话,必须要改的就是d3dpp.Windowed 这个属性的设为FALSE。至于其他参数的含义,你最好还是安装DXSDK 之后,仔细阅读它的帮助文档中的内容,那是一份最权威的资料,而且锻炼你的英文阅读,4级没过的同学可以同时学习英语和编程,一举两得啊,不过,先奉劝一 句,冷静,不要企图破坏显示器。我在上面的示例中使用的基本是默认参数,它足够让你建立起你的第一个D3D程序。
其次,顶点信息:
关于3D的知识我想大家都有一些,在很多游戏里面,反应得相当明显;例如CS是一款很优秀的FPS游戏,在3D的处理上也是很出色,不过,我们仍然能够看到人物身上的棱角;大宇的《轩辕剑4》也是这样。这就是3D图形的真正面目——三角形。一个3D对象就是有N个多边形(其实就是三角形)组成的,多边形的数量越多,对象就越圆滑,反之,则越粗糙。不过,更多的多边形将会导致,计算量增加,性能下降。这就需要我们更好的平衡性能与质量之间的关系。我个人倾向于对性能的优化,像WOW中的人物,其实它的多边形数量并不高,一方面满足了性能需求,一方面又通过一些光影效果和算法弥补了质量上的下降。好像扯远了…..
一个三角形在3D空间里就是由3个顶点组成的。由于3D空间还存在点、线等元素。所以顶点可以认为是D3D中最基本的元素。在D3D中,顶点包含的信息是很多的;但是并不是每次都会用到所有的信息。所以,我们一般会在程序中自定义顶点结构。在一个程序中是可以存在很多个顶点结构的。
struct CUSTOMVERTEX
{
FLOAT x, y, z;
DOWRD color;
};
#define D3DFVF_CUSTOMVERTEX ( D3DFVF_XYZ | D3DFVF_DIFFUSE )
struct CUSTOMVERTEX1
{
D3DXVECTOR3 position;
FLOAT tu, tv;
};
#define D3DFVF_CUSTOMVERTEX1 ( D3DFVF_XYZ | D3DFVF_TEX1 )
struct CUSTOMVERTEX2
{
D3DXVECTOR3 position;
D3DXVECTOR3 normal;
};
#define D3DFVF_CUSTOMVERTEX2 ( D3DFVF_XYZ | D3DFVF_NORMAL )
以上定义的几个顶点结构还可以合并在一起,定义成一个新的顶点结构。要注意的是每个自定义的顶点结构后面都应该有一个D3DFVF 与之相对应,对顶点结构进行说明,否则D3D是不会知道你定义的顶点到底是什么意思的。D3DFVF_XYZ代表顶点的坐标,D3DFVF_DIFFUSE代表顶点的漫反射颜色,D3DFVF_TEX1代表顶点的贴图坐标,D3DFVF_NORMAL代表顶点的法线向量。position 在顶点信息中是必须,连坐标都不知道的点对我们来说说明都不是。color 存储的是顶点的颜色信息,用一个32bit的值表示,就像 0xffff0000 表示的是不透明的红色。
第三,基本图形(图元):
在绘图软件中,都存在基本图形这一个说法;D3D同样也有基本图形,这些图元只需要提供顶点信息,便可以由D3D通过硬件迅速地绘制出来。包括有点表(Point List)、线表(Line List)、线带(List Strip)、三角形表(Triangle List)、三角形带(Triangle Strip)、三角形扇(Triangle Fan)。
示例:说明如果绘制6个单独的顶点。
CUSTOMVERTEX Vertices[] =
{
{ -5.0, -5.0, 0.0 },
{ 0.0, 5.0, 0.0 },
{ 5.0, -5.0, 0.0 },
{10.0, 5.0, 0.0},
{15.0, -5.0, 0.0 },
{20.0, 5.0, 0.0 }
};
pd3dDevice->DrawPrimitive( D3DPT_POINTLIST, 0, 6 );
在帮助文档中对每个图元有具体说明。
第四,矩阵:
哈哈哈,终于等到这一章了,其实在写上一章的时候我就已经开始迫不及待了。Matrix 听起来总是那么COOL!“Hi, Neo. Wake up …”陷入美好的回忆中,把DVD拿出来温习一遍。
OK,我回来了。矩阵是D3D重要的一个组成部分,如果你要说“废话,哪个不重要!”那么,它就是最重要的部分。没有矩阵,你的对象就没有生存的空间,重要吗?另外,矩阵对于新接触D3D的人来说,又是不太好理解的,当然,如果你的线代非常好的话,我当然没有话说,我当年可是吐出来了不少。
大家还记得左手坐标系吗?我还毕业就已经忘记了,自己去看书吧。这里只是告诉你,左手坐标系对于电脑屏幕来说,x轴的正方向是右,y轴的正方向是上,z轴的正方向是指向屏幕的。
D3D中需要设定的矩阵有3个。
I、 世界矩阵(World Matrix)
世界矩阵控制对象在3D世界中的位置偏移,偏转角度。这里要引进一个新的概念,Viewport,视口(翻译好像很土,不过大家都是这样翻译的)。后面再讨论它,先告诉你视口的大小就是等于你的程序窗体大小。世界矩阵的原点(0.0, 0.0, 0.0)就是视口的中央。为一个对象设定世界矩阵就是指定它在3D空间中的位置和偏转状态。
D3DXMATRIX matWorld;
D3DXMatrixIdentity( &matWorld );
pd3dDevice->SetTransForm( D3DTS_WORLD, &matWorld );
这样设定的对象在3D空间的位置就是原点,并且不带偏转。通常为了设定对象的偏移和转动,我们会定义两个辅助矩阵。
D3DXMATRIX matRotate; // Rotation
D3DXMatrixIdentity( &matRotate );
D3DXMatrixRotationAxis( &matRotate, AxisX, AxisY, fAngle ); // 某方向转动一定角度
D3DXMatrixMultiply( &matWorld, &matWorld, &matRotate );
D3DXMATRIX matTrans; // Translation
D3DXMatrixIdentity( &matTrans );
D3DXMatrixTranslation( &matTrans, fPosX, fPosY, fPosZ );
D3DXMatrixMultiply( &matWorld, &matWorld, &matTrans );
通过上面两个矩阵乘法得到的matWorld 就是得到了偏移量的对象的世界矩阵。
II、 观察矩阵(View Matrix)
观察矩阵说得明白一点就是,眼睛在什么地方,在看什么地方,上方是哪个方向。很简单吧。在D3D中,大家要习惯用矢量思考和计算。
D3DVECTOR3 vEyePt( fPosX, fPosY, fPosZ );
D3DVECTOR3 vLookatPt( fPosX, fPosY, fPosZ );
D3DVECTOR3 vUpVec( fX, fY, fZ );
D3DXMATRIX matView;
D3DXMatrixIdentity( &matView );
D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );
pd3dDevice->SetTransForm( D3DTS_VIEW, &matView );
关于这个好像没有什么好说的了,地球人都知道啊。