在DirectX程序中我们可以自定义顶点的数据结构并通过不同的图元类型绘制图形,本例我们使用了 D3DFVF_XYZRHW 定义顶点结构,D3DFVF_XYZRHW 与 D3DFVF_XYZ 的区别在于使用 D3DFVF_XYZRHW 即告诉D3D顶点已在屏幕坐标系中,不需要再执行视、投影、世界等变换以及进行光线计算,而直接将顶点进行光栅操作,所以在这种顶点结构下再进行 SetTransform 和 开启光照都是无效的。
我们先看以下代码:
//******************************************************************** // Filename: Main.cpp // Author: Chinafish // Modifier: Chinafish // Created: 2011-04-22 09:54 // Updated: 2011-04-22 09:54 // QQ: 149200849 // MSN: china_fish@msn.com // Purpose: Vertex buffer. //==================================================================== // Copyright(C) 2004-2011 by Chinafish. All Rights Reserved. //******************************************************************** #define STRICT #define WIN32_LEAN_AND_MEAN #include "windows.h" // Link D3D #include "d3d9.h" #include "d3dx9.h" #pragma comment(lib, "d3d9.lib") #pragma comment(lib, "d3dx9.lib") #define Release_Safe(p){if(p!=NULL) p->Release();} #define WINDOW_TITLE "www.csinx.org - Vertex Buffer (DX9+VC9)" #define WINDOW_WIDTH 800 #define WINDOW_HEIGHT 600 //----------------------------------------------------------------------------- // GLOBALS //----------------------------------------------------------------------------- HWND g_hWnd = NULL; LPDIRECT3D9 g_pD3D = NULL; LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; D3DPRESENT_PARAMETERS g_d3dpp; LPDIRECT3DVERTEXBUFFER9 g_pVertexBuffer = NULL; #define D3DFVF_MY_VERTEX ( D3DFVF_XYZRHW | D3DFVF_DIFFUSE ) struct Vertex { float x, y, z, rhw; // Position of vertex in screen coordinates DWORD color; // Color of vertex }; // // Initialize vertices // Vertex g_VertexData[] = { // x {0.0f, WINDOW_HEIGHT/2, 0.5f, 1.0f, D3DCOLOR_COLORVALUE( 1.0, 0.0, 0.0, 1.0 ) }, // x, y, z, rhw, color {WINDOW_WIDTH, WINDOW_HEIGHT/2, 0.5f, 1.0f, D3DCOLOR_COLORVALUE( 0.0, 1.0, 0.0, 1.0 ) }, // y {WINDOW_WIDTH/2, 0.0f, 0.5f, 1.0f, D3DCOLOR_COLORVALUE( 0.0, 1.0, 0.0, 1.0 ) }, {WINDOW_WIDTH/2, WINDOW_HEIGHT, 0.5f, 1.0f, D3DCOLOR_COLORVALUE( 0.0, 0.0, 1.0, 1.0 ) }, // Backslash {0.0f, 0.0f, 0.5f, 1.0f, D3DCOLOR_COLORVALUE( 1.0, 1.0, 0.0, 1.0 ) }, {WINDOW_WIDTH, WINDOW_HEIGHT, 0.5f, 1.0f, D3DCOLOR_COLORVALUE( 0.0, 1.0, 1.0, 1.0 ) }, // Slash {0.0f, WINDOW_HEIGHT, 0.5f, 1.0f, D3DCOLOR_COLORVALUE( 1.0, 0.0, 1.0, 1.0 ) }, {WINDOW_WIDTH, 0.0f, 0.5f, 1.0f, D3DCOLOR_COLORVALUE( 1.0, 1.0, 0.0, 1.0 ) } }; int g_nNumVertex = 0; //----------------------------------------------------------------------------- // PROTOTYPES //----------------------------------------------------------------------------- int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow); LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); void InitD3D(); void ShutDown(void); void Render(void); //----------------------------------------------------------------------------- // Name: WinMain() // Desc: The application's entry point //----------------------------------------------------------------------------- int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX winClass; MSG uMsg; memset(&uMsg,0,sizeof(uMsg)); winClass.lpszClassName = "CLASS_VERTEXBUFFER"; winClass.cbSize = sizeof(WNDCLASSEX); winClass.style = CS_HREDRAW | CS_VREDRAW; winClass.lpfnWndProc = WindowProc; winClass.hInstance = hInstance; winClass.hIcon = LoadIcon(hInstance, (LPCTSTR)NULL); winClass.hIconSm = LoadIcon(hInstance, (LPCTSTR)NULL); winClass.hCursor = LoadCursor(NULL, IDC_ARROW); winClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); winClass.lpszMenuName = NULL; winClass.cbClsExtra = 0; winClass.cbWndExtra = 0; if(!RegisterClassEx(&winClass)) return E_FAIL; g_hWnd = CreateWindowEx(NULL, "CLASS_VERTEXBUFFER", WINDOW_TITLE, WS_EX_TOPMOST, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, hInstance, NULL); if(g_hWnd == NULL) return E_FAIL; // Adjust window RECT rect = { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT }; AdjustWindowRect(&rect, GetWindowLong(g_hWnd, GWL_STYLE), FALSE); SetWindowPos(g_hWnd, 0, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOMOVE); ShowWindow(g_hWnd, nCmdShow); UpdateWindow(g_hWnd); // Init or reset D3D InitD3D(); while(uMsg.message != WM_QUIT) { if(PeekMessage(&uMsg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&uMsg); DispatchMessage(&uMsg); } else { // Render a frame Render(); } } ShutDown(); UnregisterClass("CLASS_VERTEXBUFFER", winClass.hInstance); return uMsg.wParam; } //----------------------------------------------------------------------------- // Name: WindowProc() // Desc: The window's message handler //----------------------------------------------------------------------------- LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_KEYDOWN: { switch(wParam) { case VK_ESCAPE: PostQuitMessage(0); break; } } break; case WM_CLOSE: { PostQuitMessage(0); } case WM_DESTROY: { PostQuitMessage(0); } break; default: { return DefWindowProc(hWnd, msg, wParam, lParam); } break; } return 0; } //----------------------------------------------------------------------------- // Name: InitD3D() // Desc: This function will only be called once during the application's // initialization phase. Therefore, it can't contain any resources that // need to be restored every time the Direct3D device is lost or the // window is resized. //----------------------------------------------------------------------------- void InitD3D() { // Create D3D g_pD3D = Direct3DCreate9(D3D_SDK_VERSION); if(!g_pD3D) { MessageBox(NULL, "Direct3DCreate9() - Failed!", "Error", NULL); return; } D3DDISPLAYMODE d3ddm; g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm); ZeroMemory(&g_d3dpp, sizeof(g_d3dpp)); g_d3dpp.Windowed = TRUE; g_d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; g_d3dpp.BackBufferFormat = d3ddm.Format; g_d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &g_d3dpp, &g_pd3dDevice); if(!g_pd3dDevice) { return; } // 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. g_nNumVertex = sizeof(g_VertexData)/sizeof(Vertex); g_pd3dDevice->CreateVertexBuffer( g_nNumVertex*sizeof(Vertex), 0, D3DFVF_MY_VERTEX, D3DPOOL_DEFAULT, &g_pVertexBuffer, NULL ); // 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. Vertex *pVertices = NULL; g_pVertexBuffer->Lock( 0, sizeof(g_VertexData), (void**)&pVertices, 0 ); memcpy( pVertices, g_VertexData, sizeof(g_VertexData) ); g_pVertexBuffer->Unlock(); } //----------------------------------------------------------------------------- // Name: ShutDown() // Desc: Release all //----------------------------------------------------------------------------- void ShutDown(void) { Release_Safe(g_pVertexBuffer); Release_Safe(g_pd3dDevice); Release_Safe(g_pD3D); } //----------------------------------------------------------------------------- // Name: Render() // Desc: Render something you want //----------------------------------------------------------------------------- void Render(void) { // Clear screen with D3DCOLOR_XRGB(0, 0, 0) g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(80, 0, 80), 1.0f, 0); g_pd3dDevice->BeginScene(); // Render geometry here... g_pd3dDevice->SetStreamSource( 0, g_pVertexBuffer, 0, sizeof(Vertex) ); g_pd3dDevice->SetFVF( D3DFVF_MY_VERTEX ); g_pd3dDevice->DrawPrimitive( D3DPT_LINELIST, 0, g_nNumVertex/2 ); g_pd3dDevice->EndScene(); g_pd3dDevice->Present(NULL, NULL, NULL, NULL); }
这里需要说明的是首先要定义顶点数据结构,这里定义了 D3DFVF_XYZRHW 和 D3DFVF_DIFFUSE 表示屏幕坐标和顶点颜色。在创建设备后通过调用 IDirect3DDevice9::CreateVertexBuffer 方法创建我们需要的顶点缓冲区。在渲染时调用 IDirect3DDevice9::SetStreamSource 方法将顶点缓冲区绑定到设备数据流上,通过调用 IDirect3DDevice9::SetFVF 方法指定当前顶点数据流声明,通过调用 IDirect3DDevice9::DrawPrimitive 方法按照当前指定的顶点格式渲染无索引的顶点序列,最后显示出来。
下图为本例代码执行效果: