欢迎访问EasyLiu的博客!
在前一章节我们已经创建了我们的第一个DirectX3D项目,但是只是显示一个灰色的窗口而已,并没有进行相应的
绘图操作。那么今天,我们就来让窗口变得更漂亮一些。
今天主要讲通过顶点缓冲区绘制图形。什么是顶点缓冲区?顶点缓冲区就是用来保存顶点数据的内存缓冲区,在DirectX3D中由COM接口IDirectVertexBuffer9表示,顶点缓冲区中顶点的数据就决定了渲染到屏幕上的效果。
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; // Buffer to hold vertices
顶点数据可以包含坐标、颜色等属性,而具体使用什么属性,可以使用灵活顶点格式(FVF)规定顶点的格式。FVF中包含了多种属性,我们可以根据我们的需要来定义我们的顶点数据格式,以逻辑或(|)连接即可,如下:
// A structure for our custom vertex type struct CUSTOMVERTEX { FLOAT x, y, z, rhw; // The transformed position for the vertex DWORD color; // The vertex color }; // Our custom FVF, which describes our custom vertex structure #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)
这里我们定义了自己的顶点格式,其中包括D3DFVF_XYZRHW(经过转换后的坐标)和D3DFVF_DIFFUSE(漫反射)。在顶点结构体中没有RHW时,Direct3D将执行视、投影、世界等变换以及进行光线计算,之后你才能在窗口中得到你所绘制的物体,这个在下一节仔细讲。当顶点结构体中有RHW时,告知Direct3D使用的顶点已经在屏幕坐标系中了,不再执行视图、投影、世界等变换和光线计算,因为D3DFVF_XYZRHW标志告诉它顶点已经经过了这些处理,并直接将顶点进行光栅操作,任何用SetTransform进行的转换都对其无效。不过这时的原点就在客户区的左上角了,其中x向右为正,y向下为正,而z的意义已经变为z-buffer的象素深度。值得注意的是D3DFVF_XYZRHW和D3DFVF_XYZ、D3DFVF_NORMAL不能共存,因为后两个标志与前一个矛盾。
定义了顶点格式之后,我们就来初始化我们的顶点缓冲区。这里,定义了一个初始化函数InitVB:
HRESULT InitVB() { // Initialize three vertices for rendering a triangle CUSTOMVERTEX vertices[] = { { 150.0f, 50.0f, 0.5f, 1.0f, 0xffff0000, }, // x, y, z, rhw, color { 250.0f, 250.0f, 0.5f, 1.0f, 0xff00ff00,}, { 50.0f, 250.0f, 0.5f, 1.0f, 0xff00ffff, }, }; // Create the vertex buffer. Here we are allocating enough memory // (from the default pool) to hold all our 3 custom vertices. We also // specify the FVF, so the vertex buffer knows what data it contains. if (FAILED(g_pd3dDevice->CreateVertexBuffer(3 * sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL))) { return E_FAIL; } // Now we fill the vertex buffer. To do this, we need to Lock() the VB to // gain access to the vertices. This mechanism is required becuase vertex // buffers may be in device memory. VOID* pVertices; if (FAILED(g_pVB->Lock(0, sizeof(vertices), (void**)&pVertices, 0))) return E_FAIL; memcpy(pVertices, vertices, sizeof(vertices)); g_pVB->Unlock(); return S_OK; }
在InitVB中,首先定义图形的顶点,在此我们初始化了一个三角形的三个顶点,存放在vertices数组中(注意:顶点的填充顺序与裁剪方式有关,默认是顺时针,可以通过g_pDevice->SetRenderState(…)进行设置,以后我们会进行讲解)。g_pDevice是有效的DirectX3D设备指针,CreateVertexBuffer方法就是创建顶点缓冲区的方法,第一个参数表示要创建的顶点缓冲区的大小(字节数),在此我们设置为数组的大小;第二个参数表示顶点缓冲区的属性,在此设置为0;第三个参数表示灵活顶点格式,设置为定义好的CUSTOMVERTEX;第四个参数表示顶点缓冲区内存类型,从枚举类型D3DPOOL(内存池)中选择,在此选择默认类型D3DPOOL_DEFAULT();第五个参数就是顶点缓冲区接口的地址;第六个参数是保留参数,在此设置为NULL。
顶点缓冲区创建好了之后,我们需要将vertexes中的数据填充进去,这时,我们需要两个函数:Lock()、Unlock()。在对顶点缓冲区进行修改时,必须先后调用加锁与解锁方法,然后通过memcpy方法,将数据拷贝到缓冲区中即可。
到此,顶点缓冲区就初始化完毕了。然后在消息循环之前调用此方法以使顶点缓冲区有效。
再就是Render()方法,
VOID Render() { // Clear the backbuffer to a blue color g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 255), 1.0f, 0); // Begin the scene if (SUCCEEDED(g_pd3dDevice->BeginScene())) { // Draw the triangles in the vertex buffer. This is broken into a few // steps. We are passing the vertices down a "stream", so first we need // to specify the source of that stream, which is our vertex buffer. Then // we need to let D3D know what vertex shader to use. Full, custom vertex // shaders are an advanced topic, but in most cases the vertex shader is // just the FVF, so that D3D knows what type of vertices we are dealing // with. Finally, we call DrawPrimitive() which does the actual rendering // of our geometry (in this case, just one triangle). g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX)); g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX); //The first parameter accepted by DrawPrimitive is a flag that tells Direct3D what type of primitives to draw //This sample uses the flag D3DPT_TRIANGLELIST to specify a list of triangles. //The second parameter is the index of the first vertex to load. //The third parameter tells the number of primitives to draw. //Because this sample draws only one triangle, this value is set to 1. //g_pd3dDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD); g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1); // End the scene g_pd3dDevice->EndScene(); } // Present the backbuffer contents to the display g_pd3dDevice->Present(NULL, NULL, NULL, NULL); }
Render()方法主要调用了三个方法SetStreamSource、SetFVF、DrawPrimitive。
SetStreamSource方法的作用是将顶点缓冲区与渲染数据流连接起来,第一个参数表示与顶点缓冲区链接的渲染数据流的编号(>=0);第二个参数表示顶点缓冲区接口指针;第三个参数表示渲染数据流的起始位置;第四个参数表示相邻顶点距离的字节数,在此为VERTEX结构体CUSTOMVERTEX的大小。
SetFVF方法设置渲染数据流的灵活顶点格式,在此设置自己定义的CUSTOMVERTEX。
DrawPrimitive就是用来绘制渲染数据流中图元的方法。其中有三个参数,分别为,第一个参数表示渲染的图元类型,在此为D3DPT_TRIANGLELIST(三角形列表);第二个参数表示从顶点缓冲区的第几个顶点开始绘制;第三个参数表示绘制的图元的数量。
最后别忘了在Cleanup方法中将g_pVB接口Release掉,
VOID Cleanup() { if (g_pVB != NULL) g_pVB->Release(); if (g_pd3dDevice != NULL) g_pd3dDevice->Release(); if (g_pD3D != NULL) g_pD3D->Release(); }
保存修改,按Ctrl+F5运行项目,效果如下:
版权声明:本文为博主原创文章,未经博主允许不得转载。