下面我们来填写代码,首先是初始化,我们在InitD3D函数里填写下面的代码:
g_pD3D = Direct3DCreate9( D3D_SDK_VERSION );
if( NULL == g_pD3D )
{
return E_FAIL;
}
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof(d3dpp) );
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hWnd,
D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE,
&d3dpp, &g_pd3dDevice );
g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE );
g_pd3dDevice->SetRenderState( D3DRS_AMBIENT,
D3DCOLOR_COLORVALUE( 0.3f, 0.3f, 0.3f, 1.0 ) );
g_pd3dDevice->LightEnable( 0, TRUE);
D3DMATERIAL9 mtrl;
ZeroMemory( &mtrl, sizeof(mtrl) );
mtrl.Diffuse.r = mtrl.Ambient.r = 140.0f / 255.0f;
mtrl.Diffuse.g = mtrl.Ambient.g = 200.0f / 255.0f;
mtrl.Diffuse.b = mtrl.Ambient.b = 255.0f / 255.0f;
mtrl.Diffuse.a = mtrl.Ambient.a = 1.0f;
g_pd3dDevice->SetMaterial( &mtrl );
return S_OK; | Direct3DCreate9函数为我们创建了一个D3D接口指针,并将接口指针返回到全局变量中保存起来,参数必须为D3D_SDK_VERSION。D3DPRESENT_PARAMETERS是一个结构体,它就像OpenGL中的PixelFormat一样,是创建时指定设备的一些属性的。
d3dpp.Windowed = TRUE; // 设备是窗口设备而不是全屏 d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; // 翻转缓冲区时不改动后台缓冲 d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8; // ARGB颜色模式 | 在这之后,我们就可以调用CreateDevice来创建设备了,第一个参数指定使用主显示驱动,第二个参数指定该设备是否使用硬件来处理显示,第三个参数当然是你的窗体句柄,第四个参数如果指定D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE效率会提高不少。
g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE ); g_pd3dDevice->SetRenderState( D3DRS_AMBIENT, D3DCOLOR_COLORVALUE( 0.6f, 0.6f, 0.6f, 1.0 ) ); | 这两句用于打开灯光和设置环境光照颜色。接着我设置了材质:
mtrl.Diffuse.r = mtrl.Ambient.r = 140.0f / 255.0f; mtrl.Diffuse.g = mtrl.Ambient.g = 200.0f / 255.0f; mtrl.Diffuse.b = mtrl.Ambient.b = 255.0f / 255.0f; mtrl.Diffuse.a = mtrl.Ambient.a = 1.0f; | 为了和上一篇的例程做对比,我使用了相同的材质。当CreateDevice执行成功后,我们就该创建基于D3D的三维物体了。
D3DVECTOR SrcBox[] = { { 5.0f, 5.0f, 0.0f }, { 5.0f, 5.0f, 10.0f }, { 5.0f, -5.0f, 0.0f }, { 5.0f, -5.0f, 10.0f }, {-5.0f, -5.0f, 0.0f }, {-5.0f, -5.0f, 10.0f }, {-5.0f, 5.0f, 0.0f }, {-5.0f, 5.0f, 10.0f }, };
WORD wIndex[] ={ 0, 4, 6, 0, 2, 4, 0, 6, 7, 0, 7, 1, 0, 3, 2, 0, 1, 3, 5, 2, 3, 5, 4, 2, 5, 6, 4, 5, 7, 6, 5, 1, 7, 5, 3, 1, }; | 要说明的是D3D为我们准备了很好用的结构体D3DVECTOR,封装了三维座标的X、Y和Z。我们还需要再定义一个我们自己的结构体来存放我们的三维座标信息:
struct CUSTOMVERTEX { D3DVECTOR pos; D3DVECTOR normal; }; | 第一个成员pos用来存储顶点座标数据,第二个成员normal用来存储这个点所在平面的法向量——这个概念我在上一篇讲过的。和OpenGL一样,我们同样需要按索引序展开顶点数组:
CUSTOMVERTEX ExpandBox[sizeof(wIndex) / sizeof(WORD)]; for ( int i = 0; i < 36; i++ ) { ExpandBox[i].pos = SrcBox[ wIndex[i] ]; } | 然后用下面的代码为顶点计算法向量。
for ( i = 0; i < 12; i++ ) { D3DVECTOR Tri[3]; Tri[0] = ExpandBox[ i * 3 + 0 ].pos; Tri[1] = ExpandBox[ i * 3 + 1 ].pos; Tri[2] = ExpandBox[ i * 3 + 2 ].pos; ExpandBox[ i * 3 + 0 ].normal.x = 0.0f; ExpandBox[ i * 3 + 0 ].normal.y = 0.0f; ExpandBox[ i * 3 + 0 ].normal.z = 1.0f; CalcNormal( Tri, &(ExpandBox[ i * 3 + 0 ].normal) ); ExpandBox[ i * 3 + 1 ].normal = ExpandBox[ i * 3 + 0 ].normal; ExpandBox[ i * 3 + 2 ].normal = ExpandBox[ i * 3 + 0 ].normal; }
| 在这里我需要花点篇幅讲一个概念,一个仅在D3D中存在的概念FVF。D3D在处理显示数据时和OpenGL的方式不大一样,OpenGL是指定独立的数组,比如VertexBuffer、IndexBuffer、NormalBuffer等,而D3D是把每一个顶点的这些数据放在一起,组成一个单元进行处理,也就是说只有一个数组,而在数组的每一个元素中都包括了单个顶点的所有数据,所以他被称为Flexible Vertex Format,我刚才定义的结构体CUSTOMVERTEX也就是做为数组的一个单元。每一个单元可以包含你所需要的信息,比如你只需要顶点座标数据和颜色,那么你只需要对那个结构体稍加修改就可以了,但是怎么让D3D 知道你的结构体里包含哪些数据呢?当然,我们可以在CreateVertexBuffer的时候指定一个参数来告诉D3D:D3DFVF_XYZ | D3DFVF_NORMAL,在很多地方我们都可能用到这个值,所以为了方便使用和维护我们定义一个宏来表示它:
| #define D3DFVF_CUSTOMVERTEX ( D3DFVF_XYZ | D3DFVF_NORMAL ) |
|
|