在DirectX程序中我们可以自定义多个不同数据格式的顶点缓冲区,本例我们定义了三个顶点缓冲区(在DX9中应该最大可以支持16个)存放顶点位置,颜色和纹理的坐标。通过鼠标拖曳旋转物体,按键1和2控制纹理的采样方式最近点采样(最近点采样和线性纹理过滤)
我们先看以下代码:
//******************************************************************** // Filename: Main.cpp // Author: Chinafish // Modifier: Chinafish // Created: 2011-05-11 16:52 // Updated: 2011-05-11 16:52 // QQ: 149200849 // MSN: china_fish@msn.com // Purpose: Multiple 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 - Multiple 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; LPDIRECT3DTEXTURE9 g_pTexture = NULL; D3DPRESENT_PARAMETERS g_d3dpp; LPDIRECT3DVERTEXBUFFER9 g_pVertexBuffer = NULL; LPDIRECT3DVERTEXBUFFER9 g_pColorBuffer = NULL; LPDIRECT3DVERTEXBUFFER9 g_pTexCoordBuffer = NULL; LPDIRECT3DVERTEXDECLARATION9 g_pVertexDeclaration; float g_fSpinX = 0.0f; float g_fSpinY = 0.0f; int g_nNumVertex = 0; // 图片数据 BYTE g_TexData[] = { 0x42, 0x4D, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0 }; struct Vertex { float x, y, z; }; // 顶点位置 Vertex g_VertexData[] = { {-1.0f, 1.0f,-1.0f }, { 1.0f, 1.0f,-1.0f }, {-1.0f,-1.0f,-1.0f }, { 1.0f,-1.0f,-1.0f }, {-1.0f, 1.0f, 1.0f }, {-1.0f,-1.0f, 1.0f }, { 1.0f, 1.0f, 1.0f }, { 1.0f,-1.0f, 1.0f }, {-1.0f, 1.0f, 1.0f }, { 1.0f, 1.0f, 1.0f }, {-1.0f, 1.0f,-1.0f }, { 1.0f, 1.0f,-1.0f }, {-1.0f,-1.0f, 1.0f }, {-1.0f,-1.0f,-1.0f }, { 1.0f,-1.0f, 1.0f }, { 1.0f,-1.0f,-1.0f }, { 1.0f, 1.0f,-1.0f }, { 1.0f, 1.0f, 1.0f }, { 1.0f,-1.0f,-1.0f }, { 1.0f,-1.0f, 1.0f }, {-1.0f, 1.0f,-1.0f }, {-1.0f,-1.0f,-1.0f }, {-1.0f, 1.0f, 1.0f }, {-1.0f,-1.0f, 1.0f } }; struct Color { DWORD color; }; // 顶点颜色 Color g_VertexColor[] = { { D3DCOLOR_COLORVALUE( 1.0, 0.0, 0.0, 1.0 ) }, { D3DCOLOR_COLORVALUE( 1.0, 0.0, 0.0, 1.0 ) }, { D3DCOLOR_COLORVALUE( 1.0, 0.0, 0.0, 1.0 ) }, { D3DCOLOR_COLORVALUE( 1.0, 0.0, 0.0, 1.0 ) }, { D3DCOLOR_COLORVALUE( 0.0, 1.0, 0.0, 1.0 ) }, { D3DCOLOR_COLORVALUE( 0.0, 1.0, 0.0, 1.0 ) }, { D3DCOLOR_COLORVALUE( 0.0, 1.0, 0.0, 1.0 ) }, { D3DCOLOR_COLORVALUE( 0.0, 1.0, 0.0, 1.0 ) }, { D3DCOLOR_COLORVALUE( 0.0, 0.0, 1.0, 1.0 ) }, { D3DCOLOR_COLORVALUE( 0.0, 0.0, 1.0, 1.0 ) }, { D3DCOLOR_COLORVALUE( 0.0, 0.0, 1.0, 1.0 ) }, { D3DCOLOR_COLORVALUE( 0.0, 0.0, 1.0, 1.0 ) }, { D3DCOLOR_COLORVALUE( 1.0, 1.0, 0.0, 1.0 ) }, { D3DCOLOR_COLORVALUE( 1.0, 1.0, 0.0, 1.0 ) }, { D3DCOLOR_COLORVALUE( 1.0, 1.0, 0.0, 1.0 ) }, { D3DCOLOR_COLORVALUE( 1.0, 1.0, 0.0, 1.0 ) }, { D3DCOLOR_COLORVALUE( 1.0, 0.0, 1.0, 1.0 ) }, { D3DCOLOR_COLORVALUE( 1.0, 0.0, 1.0, 1.0 ) }, { D3DCOLOR_COLORVALUE( 1.0, 0.0, 1.0, 1.0 ) }, { D3DCOLOR_COLORVALUE( 1.0, 0.0, 1.0, 1.0 ) }, { D3DCOLOR_COLORVALUE( 0.0, 1.0, 1.0, 1.0 ) }, { D3DCOLOR_COLORVALUE( 0.0, 1.0, 1.0, 1.0 ) }, { D3DCOLOR_COLORVALUE( 0.0, 1.0, 1.0, 1.0 ) }, { D3DCOLOR_COLORVALUE( 0.0, 1.0, 1.0, 1.0 ) } }; struct TexCoord { float tu, tv; }; // 纹理坐标 TexCoord g_TexCoord[] = { { 0.0f, 0.0f }, { 1.0f, 0.0f }, { 0.0f, 1.0f }, { 1.0f, 1.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f }, { 0.0f, 0.0f }, { 0.0f, 1.0f }, { 0.0f, 0.0f }, { 1.0f, 0.0f }, { 0.0f, 1.0f }, { 1.0f, 1.0f }, { 0.0f, 0.0f }, { 1.0f, 0.0f }, { 0.0f, 1.0f }, { 1.0f, 1.0f }, { 0.0f, 0.0f }, { 1.0f, 0.0f }, { 0.0f, 1.0f }, { 1.0f, 1.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f }, { 0.0f, 0.0f }, { 0.0f, 1.0f } }; //----------------------------------------------------------------------------- // 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_MULTIVERTEXBUFFER"; 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_MULTIVERTEXBUFFER", 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_MULTIVERTEXBUFFER", winClass.hInstance); return uMsg.wParam; } //----------------------------------------------------------------------------- // Name: WindowProc() // Desc: The window's message handler //----------------------------------------------------------------------------- LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { static POINT ptLastMousePos; static POINT ptCurrentMousePos; static bool bDraging; switch(msg) { case WM_KEYDOWN: { switch(wParam) { case VK_ESCAPE: PostQuitMessage(0); break; case 0x31: // 1 { // 最近点采样 g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_POINT ); g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT ); } break; case 0x32: // 2 { // 线性纹理过滤 g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR ); g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR ); } break; } } break; case WM_LBUTTONDOWN: { ptLastMousePos.x = ptCurrentMousePos.x = LOWORD (lParam); ptLastMousePos.y = ptCurrentMousePos.y = HIWORD (lParam); bDraging = true; } break; case WM_LBUTTONUP: { bDraging = false; } break; case WM_MOUSEMOVE: { ptCurrentMousePos.x = LOWORD (lParam); ptCurrentMousePos.y = HIWORD (lParam); if( bDraging ) { g_fSpinX -= (ptCurrentMousePos.x - ptLastMousePos.x); g_fSpinY -= (ptCurrentMousePos.y - ptLastMousePos.y); } ptLastMousePos.x = ptCurrentMousePos.x; ptLastMousePos.y = ptCurrentMousePos.y; } 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.EnableAutoDepthStencil = TRUE; g_d3dpp.AutoDepthStencilFormat = D3DFMT_D16; 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; } g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE ); g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE ); D3DXMATRIX matProj; D3DXMatrixPerspectiveFovLH( &matProj, D3DXToRadian( 45.0f ), 640.0f / 480.0f, 0.1f, 100.0f ); g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj ); // Create Texture D3DXCreateTextureFromFileInMemory( g_pd3dDevice, &g_TexData, sizeof(g_TexData)/sizeof(BYTE), &g_pTexture); if(!g_pTexture) { MessageBox(NULL, "D3DXCreateTextureFromFileInMemory() - Failed!", "Error", NULL); return; } g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR ); g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR ); // 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, 0, 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(); // Create a vertex buffer that contains only the color data g_nNumVertex = sizeof(g_VertexColor)/sizeof(Color); g_pd3dDevice->CreateVertexBuffer( g_nNumVertex*sizeof(Color), 0, 0, D3DPOOL_DEFAULT, &g_pColorBuffer, NULL ); void *pColors = NULL; g_pColorBuffer->Lock( 0, sizeof(g_VertexColor), (void**)&pColors, 0 ); memcpy( pColors, g_VertexColor, sizeof(g_VertexColor) ); g_pColorBuffer->Unlock(); // Create a vertex buffer that contains only the texture coordinate data g_nNumVertex = sizeof(g_TexCoord)/sizeof(TexCoord); g_pd3dDevice->CreateVertexBuffer( g_nNumVertex*sizeof(TexCoord), 0, 0, D3DPOOL_DEFAULT, &g_pTexCoordBuffer, NULL ); void *pTexCoords = NULL; g_pTexCoordBuffer->Lock( 0, sizeof(g_TexCoord), (void**)&pTexCoords, 0 ); memcpy( pTexCoords, g_TexCoord, sizeof(g_TexCoord) ); g_pTexCoordBuffer->Unlock(); // Create a vertex declaration so we can describe to Direct3D how we'll be passing our data to it. D3DVERTEXELEMENT9 dwDecl[] = { // Stream Offset Type Method Usage UsageIndex { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }, { 1, 0, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 }, { 2, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 }, D3DDECL_END() }; // 创建顶点着色声明 g_pd3dDevice->CreateVertexDeclaration( dwDecl, &g_pVertexDeclaration ); } //----------------------------------------------------------------------------- // Name: ShutDown() // Desc: Release all //----------------------------------------------------------------------------- void ShutDown(void) { Release_Safe(g_pVertexBuffer); Release_Safe(g_pColorBuffer); Release_Safe(g_pTexCoordBuffer); Release_Safe(g_pTexture); Release_Safe(g_pd3dDevice); Release_Safe(g_pD3D); } //----------------------------------------------------------------------------- // Name: Render() // Desc: Render something you want //----------------------------------------------------------------------------- void Render(void) { // Clear screen with D3DCOLOR_XRGB(80, 0, 80) g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(80, 0, 80), 1.0f, 0); D3DXMATRIX matTrans; D3DXMATRIX matRot; D3DXMATRIX matWorld; D3DXMatrixTranslation( &matTrans, 0.0f, 0.0f, 5.0f ); D3DXMatrixRotationYawPitchRoll( &matRot, D3DXToRadian(g_fSpinX), D3DXToRadian(g_fSpinY), 0.0f ); matWorld = matRot * matTrans; g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld ); g_pd3dDevice->BeginScene(); // Render geometry here... g_pd3dDevice->SetTexture( 0, g_pTexture ); g_pd3dDevice->SetVertexDeclaration( g_pVertexDeclaration ); g_pd3dDevice->SetStreamSource( 0, g_pVertexBuffer, 0, sizeof(Vertex) ); g_pd3dDevice->SetStreamSource( 1, g_pColorBuffer, 0, sizeof(Color) ); g_pd3dDevice->SetStreamSource( 2, g_pTexCoordBuffer, 0, sizeof(TexCoord) ); g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 ); g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 4, 2 ); g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 8, 2 ); g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 12, 2 ); g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 16, 2 ); g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 20, 2 ); g_pd3dDevice->EndScene(); g_pd3dDevice->Present(NULL, NULL, NULL, NULL); }
这里需要说明的是首先定义了图片数据以及顶点的位置数据,颜色数据和纹理坐标数据。 再按以前的步骤创建DX应用程序,在创建设备后通过调用 D3DXCreateTextureFromFileInMemory 方法从内存图片数据创建纹理,通过调用 IDirect3DDevice9::CreateVertexBuffer 方法创建多个我们需要格式的顶点缓冲区,通过调用 IDirect3DDevice9::CreateVertexDeclaration 方法创建顶点着色声明。在渲染时调用 IDirect3DDevice9::SetTexture 设定纹理状态,调用 IDirect3DDevice9::SetVertexDeclaration 设定顶点数据流声明,再通过多次调用 IDirect3DDevice9::SetStreamSource 方法将不同的顶点缓冲区绑定到多个设备数据流上,最后通过调用 IDirect3DDevice9::DrawPrimitive 方法按照当前指定的顶点格式渲染无索引的顶点序列,最后显示出来。
下图为本例代码执行效果: