Direct3D 顶点缓存
今天我们来学习下Direct3D的顶点和顶点缓存,首先我们需要在场景中绘制一些物体,物体都是由多个三角形组成,每一个三角形由三个顶点组成,我们来看下面一个NPC的模型
左图:正常的模型 右图:看的出模型是有多个三角形组成
现在我们知道了一个模型最小单位是一个顶点. 如果我们需要自己绘制物体,就要学习下Direct3D如何创建顶点. 顶点在Direct3D中叫顶点缓存(VertexBuffer),顶点缓存保存了顶点的一些数据空间,比如位置,颜色,法向量等等.
定制顶点缓存(FVF)格式:
刚刚说了顶点有很多信息,有的时候我们只需要一些信息,根据自己需要定制顶点缓存
//坐标位置和颜色的顶点 struct CUSTOMVERTEX1 { float x,y,z; DWORD color; }; //等下创建顶点缓存,要传入这个宏,Direct3D才知道我们用的是那种顶点 #define D3DFVF_CUSTOMVERTEX1 (D3DFVF_XYZRHW | D3DFVF_DIFFUSE); //坐标位置和法向量的顶点 struct CUSTOMVERTEX2 { float x,y,z; float nx,ny,nz; //法向量 float u,v; //纹理坐标 }; #define D3DFVF_CUSTOMVERTEX2 (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1);
FVF常用格式
D3DFVF_XYZ | 未经过坐标变换的顶点坐标值,不可以和D3DFVF_XYZRHW一起使用 |
D3DFVF_XYZRHW | 经过坐标变换的顶点坐标值,不可以和D3DFVF_XYZ,D3DFVF_NORMAL一起使用 |
D3DFVF_XYZB1~5 | 顶点混合的权重值 |
D3DFVF_NORMAL | 法线向量的数值 |
D3DFVF_DIFFUSE | 漫反射的颜色值 |
D3DFVF_SPECULAR | 镜面反射的数值 |
D3DFVF_TEX1~8 | 1~8纹理坐标的信息(纹理=贴图) |
顶点格式的顺序原则:
顶点坐标位置->RHW值->顶点混合权重值->顶点法向量->漫反射颜色值->镜面反射颜色值->纹理坐标信息
创建顶点缓存:
LPDIRECT3DVERTEXBUFFER9 g_pVertexBuffer = NULL; //顶点缓存对象 Device->CreateVertexBuffer(4*sizeof(CUSTOMVERTEX1), 0 , D3DFVF_CUSTOMVERTEX1 , D3DPOOL_DEFAULT,&g_pVertexBuffer,NULL);
访问顶点缓存:
//创建顶点缓存 if(FAILED(g_pd3dDevice->CreateVertexBuffer(3*sizeof(CUSTOMVERTEX),0,D3DFVF_CUSTOMVERTEX,D3DPOOL_DEFAULT,&g_pVertexBuffer,NULL))) { return E_FAIL; } //创建顶点数据 CUSTOMVERTEX vertices [] = { { 100.0f, 100.0f, 0.0f, 1.0f, D3DCOLOR_XRGB(0, 0,0), }, { 300.0f, 100.0f, 0.0f, 1.0f, D3DCOLOR_XRGB(255, 255, 255), }, { 300.0f, 300.0f, 0.0f, 1.0f, D3DCOLOR_XRGB(0,0,0), }, }; //填充顶点缓冲区 VOID *pVertices; if(FAILED(g_pVertexBuffer->Lock(0,sizeof(vertices),(void**)&pVertices,0))) //加锁 { return E_FAIL; } memcpy(pVertices,vertices,sizeof(vertices)); //把顶点数据保存到顶点缓存中 g_pVertexBuffer->Unlock(); //解锁 g_pd3dDevice->SetRenderState(D3DRS_CULLMODE,false);
图形的绘制: 上面我们已经准备好了顶点数据,现在只需要把顶点绘制到屏幕上就可以啦,绘制图形都是的BeginScene() 和 EndScene()函数中间,接下来我们需要依次调用以下函数,、
1. IDirect3DDevice9::SetStreamSource() 设置顶点源
2. IDirect3DDevice9::SetFVF() 设置灵活顶点格式
3. IDirect3DDevice9::DrawPrimitive() 绘制图形
g_pd3dDevice->SetStreamSource(0,g_pVertexBuffer,0,sizeof(CUSTOMVERTEX)); //设置绘制的顶点数据 g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX); //设置灵活顶点格式 g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST,0,2); //绘制两个三角形
效果图:
全部代码:
#include <d3d9.h> #include <d3dx9.h> #include <tchar.h> #pragma comment(lib,"winmm.lib") #pragma comment(lib,"d3d9.lib") #pragma comment(lib,"d3dx9.lib") #define WINDOW_WIDTH 800 #define WINDOW_HEIGHT 600 #define WINDOW_TITLE L"盘子脸的程序" #define SAFE_RELEASE(p) { if(p) { (p) -> Release();(p)=NULL;} } struct CUSTOMVERTEX { float x,y,z,rhw; DWORD color; }; #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW | D3DFVF_DIFFUSE) //声明变量 LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; ID3DXFont* g_pFont = NULL; float g_FPS = 0.0f; wchar_t g_strFPS[50]; //包含帧速率的字符 LPDIRECT3DVERTEXBUFFER9 g_pVertexBuffer = NULL; //顶点缓冲区对象 //全局函数声明部分 LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam); HRESULT Direct3DInit(HWND hwnd); HRESULT ObjectsInit(HWND hwnd); VOID Direct3DRender(HWND hwnd); VOID Direct3DCleanUp(); //主函数 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd) { //设置一个完整的窗口类,没有设置图标 WNDCLASSEX wndClass = {0}; wndClass.cbSize = sizeof(WNDCLASSEX); wndClass.style = CS_HREDRAW | CS_VREDRAW; //设置窗口样式 wndClass.lpfnWndProc = WndProc; wndClass.cbClsExtra = 0; wndClass.cbWndExtra = 0; wndClass.hInstance = hInstance; wndClass.hCursor = LoadCursor(NULL,IDC_ARROW); wndClass.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH); wndClass.lpszMenuName = NULL; wndClass.lpszClassName = L"GameDevelop"; //注册窗口类 if(!RegisterClassEx(&wndClass)) { return -1; } //正式创建窗口 HWND hwnd = CreateWindow(L"GameDevelop",WINDOW_TITLE,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,WINDOW_WIDTH, WINDOW_HEIGHT,NULL,NULL,hInstance,NULL); //初始化Direct3D资源 if(!(S_OK == Direct3DInit(hwnd))) { MessageBox(hwnd,_T("初始化Direct3D失败"),_T("盘子脸的消息窗口"),0); } MoveWindow(hwnd,250,80,WINDOW_WIDTH,WINDOW_HEIGHT,true); ShowWindow(hwnd,nShowCmd); UpdateWindow(hwnd); //消息循环过程 MSG msg = {0}; while(msg.message != WM_QUIT) { if(PeekMessage(&msg,0,0,0,PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); }else { Direct3DRender(hwnd); } } //窗口类的注销 UnregisterClass(L"GameDevelop",wndClass.hInstance); return 0; } //消息处理函数 LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) { switch (message) { case WM_PAINT: Direct3DRender(hwnd); ValidateRect(hwnd,NULL); break; case WM_KEYDOWN: if(wParam == VK_ESCAPE) { DestroyWindow(hwnd); } break; case WM_DESTROY: Direct3DCleanUp(); //失败Direct3D资源 PostQuitMessage(0); break; default: return DefWindowProc(hwnd,message,wParam,lParam); } return 0; } HRESULT Direct3DInit(HWND hwnd) { //创建Direct3D接口对象 LPDIRECT3D9 pD3D = NULL; if(NULL == (pD3D = Direct3DCreate9(D3D_SDK_VERSION))) { return E_FAIL; } //获取硬件消息 D3DCAPS9 caps; int vp = 0; if(FAILED(pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,&caps))) { return E_FAIL; } if(caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) { //支出硬件顶点运算 vp = D3DCREATE_HARDWARE_VERTEXPROCESSING; } else { vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING; } //填充Direct3Dpresent参数 D3DPRESENT_PARAMETERS d3dpp; ZeroMemory(&d3dpp,sizeof(d3dpp)); d3dpp.BackBufferWidth = WINDOW_WIDTH; d3dpp.BackBufferHeight = WINDOW_HEIGHT; d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8; d3dpp.BackBufferCount = 1; d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE; d3dpp.MultiSampleQuality = 0; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.hDeviceWindow = hwnd; d3dpp.Windowed = true; d3dpp.EnableAutoDepthStencil = true; d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8; d3dpp.Flags = 0; d3dpp.FullScreen_RefreshRateInHz = 0; d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; //创建Direct3D设备接口 if(FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,hwnd,vp,&d3dpp,&g_pd3dDevice))) { return E_FAIL; } SAFE_RELEASE(pD3D); if(!(S_OK == ObjectsInit(hwnd))) { return E_FAIL; } return S_OK; } HRESULT ObjectsInit(HWND hwnd) { //创建字体 if(FAILED(D3DXCreateFont(g_pd3dDevice,36,0,0,1,false,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,DEFAULT_QUALITY,0,_T("微软雅黑"),&g_pFont))) { return E_FAIL; } //创建顶点缓存 if(FAILED(g_pd3dDevice->CreateVertexBuffer(3*sizeof(CUSTOMVERTEX),0,D3DFVF_CUSTOMVERTEX,D3DPOOL_DEFAULT,&g_pVertexBuffer,NULL))) { return E_FAIL; } //创建顶点数据 CUSTOMVERTEX vertices [] = { { 100.0f, 100.0f, 0.0f, 1.0f, D3DCOLOR_XRGB(0, 0,0), }, { 300.0f, 100.0f, 0.0f, 1.0f, D3DCOLOR_XRGB(255, 255, 255), }, { 300.0f, 300.0f, 0.0f, 1.0f, D3DCOLOR_XRGB(0,0,0), }, }; //填充顶点缓冲区 VOID *pVertices; if(FAILED(g_pVertexBuffer->Lock(0,sizeof(vertices),(void**)&pVertices,0))) //加锁 { return E_FAIL; } memcpy(pVertices,vertices,sizeof(vertices)); //把顶点数据保存到顶点缓存中 g_pVertexBuffer->Unlock(); //解锁 g_pd3dDevice->SetRenderState(D3DRS_CULLMODE,false); return S_OK; } void Direct3DRender(HWND hwnd) { g_pd3dDevice->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0); //定义一个矩形 RECT formatRect; GetClientRect(hwnd,&formatRect); g_pd3dDevice->BeginScene(); g_pd3dDevice->SetRenderState(D3DRS_SHADEMODE,D3DSHADE_GOURAUD); //绘制图形 g_pd3dDevice->SetStreamSource(0,g_pVertexBuffer,0,sizeof(CUSTOMVERTEX)); //设置绘制的顶点数据 g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX); //设置灵活顶点格式 g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST,0,2); //绘制两个三角形 //绘制文本信息 g_pFont->DrawTextW(NULL,L"盘子脸",3,&formatRect,DT_TOP | DT_RIGHT , D3DCOLOR_XRGB(255,39,136)); g_pd3dDevice->EndScene(); g_pd3dDevice->Present(NULL,NULL,NULL,NULL); } void Direct3DCleanUp() { SAFE_RELEASE(g_pVertexBuffer); SAFE_RELEASE(g_pFont); SAFE_RELEASE(g_pd3dDevice); }
如果你感兴趣,你可以把你妹妹介绍给我