DirectX描述物体使用三角形单元,构成三角形的最基本单位是顶点。
DirectX中顶点格式是很灵活的,即我们可以自己定义顶点所包含的信息。除了坐标之外,我们还需要定义其他附加属性,颜色属性,法线属性等等。
我们在定义的时候,首先要使用DX的一个宏声明一下我们所定义的顶点包含哪些属性。
//------------绘制图形步骤1.定义灵活顶点格式 #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)//坐标为经过变换的屏幕坐标,顶点的颜色
然后我们就可以定义一个包含我们需要的属性的结构体了:
//------------绘制图形步骤2.根据上面定义的顶点格式,创建一个顶点的结构体 struct stVertex { D3DXVECTOR4 vPos; //位置坐标 DWORD dwColor; //颜色 };然后我们需要声明一个指向顶点缓冲区的指针:
//----------绘制图形步骤3.声明一个顶点缓冲区指针 LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;
//----------绘制图形步骤4.定义一个结构体数组用来给每个顶点赋值 //数组中存储当前程序中顶点的数据 stVertex vertex[] = { {D3DXVECTOR4(100.0f, 100.0f, 0.0f, 1.0f), D3DCOLOR_XRGB(255,0,0)}, {D3DXVECTOR4(400.0f, 100.0f, 0.0f, 1.0f), D3DCOLOR_XRGB(0,255,0)}, {D3DXVECTOR4(100.0f, 400.0f, 0.0f, 1.0f), D3DCOLOR_XRGB(0,0,255)} };
//----------绘制图形步骤5.为定点缓冲区分配内存,并将数组中的顶点值拷贝到顶点缓冲区中 //通过设备指针来创建顶点缓冲区,用来存储顶点数据 g_pDevice->CreateVertexBuffer( sizeof(vertex), //顶点缓冲区大小 D3DUSAGE_WRITEONLY, //顶点缓冲区作用 D3DFVF_CUSTOMVERTEX, //通知系统顶点格式 D3DPOOL_MANAGED, //顶点缓冲区存储位置,此处表示由系统处理 &g_pVB, //返回顶点缓冲区指针 NULL //系统保留参数,NULL ); void* pVertices = NULL; //锁定顶点缓冲区,向其中拷贝数据 g_pVB->Lock( 0, //锁定的偏移量 sizeof(vertex), //锁定的大小 &pVertices, //锁定之后存储空间 0 //锁定的标识,0 ); //将数组中的内容拷贝到缓冲区中 memcpy(pVertices, vertex, sizeof(vertex)); //解锁 g_pVB->Unlock();
//----------绘制图形步骤6.设置数据源,设置灵活顶点格式,绘制图元 //设置数据流来源 g_pDevice->SetStreamSource( 0, //数据流管道号(0-15) g_pVB, //数据来源 0, //数据流偏移量 sizeof(stVertex) //每个数据的字节数大小 ); //通知系统数据格式,以便解析数据 g_pDevice->SetFVF(D3DFVF_CUSTOMVERTEX); //绘制图元 g_pDevice->DrawPrimitive( D3DPT_TRIANGLELIST, //三角形列 0, //起始点编号 1 //图元数量 );
总结一下在D3D中绘制图形的步骤:
1.定义灵活顶点格式
2.根据顶点格式构建一个描述顶点的结构体
3.声明一个顶点缓冲区指针
4.定义一个顶点结构数组用来给每个顶点赋值
5.为顶点缓冲区分配空间并将数组中的顶点数据拷贝到顶点缓冲区
6.设置数据源,设置灵活顶点格式,绘制图元
完整的代码:
// D3DDemo.cpp : 定义应用程序的入口点。 // #include "stdafx.h" #include "D3DDemo.h" #define MAX_LOADSTRING 100 // 全局变量: HINSTANCE hInst; // 当前实例 TCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本 TCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名 // 此代码模块中包含的函数的前向声明: HWND g_hWnd; ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //---------改造3D窗口需要的内容------------ LPDIRECT3D9 g_pD3D = NULL; //D3D接口指针 LPDIRECT3DDEVICE9 g_pDevice = NULL;//D3D设备指针 //------------绘制图形步骤1.定义灵活顶点格式 #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)//坐标为经过变换的屏幕坐标,顶点的颜色 //------------绘制图形步骤2.根据上面定义的顶点格式,创建一个顶点的结构体 struct stVertex { D3DXVECTOR4 vPos; //位置坐标 DWORD dwColor; //颜色 }; //----------绘制图形步骤3.声明一个顶点缓冲区指针 LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; //初始化顶点缓冲区 void initVB() { //----------绘制图形步骤4.定义一个结构体数组用来给每个顶点赋值 //数组中存储当前程序中顶点的数据 stVertex vertex[] = { {D3DXVECTOR4(100.0f, 100.0f, 0.0f, 1.0f), D3DCOLOR_XRGB(255,0,0)}, {D3DXVECTOR4(400.0f, 100.0f, 0.0f, 1.0f), D3DCOLOR_XRGB(0,255,0)}, {D3DXVECTOR4(100.0f, 400.0f, 0.0f, 1.0f), D3DCOLOR_XRGB(0,0,255)} }; //----------绘制图形步骤5.为定点缓冲区分配内存,并将数组中的顶点值拷贝到顶点缓冲区中 //通过设备指针来创建顶点缓冲区,用来存储顶点数据 g_pDevice->CreateVertexBuffer( sizeof(vertex), //顶点缓冲区大小 D3DUSAGE_WRITEONLY, //顶点缓冲区作用 D3DFVF_CUSTOMVERTEX, //通知系统顶点格式 D3DPOOL_MANAGED, //顶点缓冲区存储位置,此处表示由系统处理 &g_pVB, //返回顶点缓冲区指针 NULL //系统保留参数,NULL ); void* pVertices = NULL; //锁定顶点缓冲区,向其中拷贝数据 g_pVB->Lock( 0, //锁定的偏移量 sizeof(vertex), //锁定的大小 &pVertices, //锁定之后存储空间 0 //锁定的标识,0 ); //将数组中的内容拷贝到缓冲区中 memcpy(pVertices, vertex, sizeof(vertex)); //解锁 g_pVB->Unlock(); } void onCreatD3D() { g_pD3D = Direct3DCreate9(D3D_SDK_VERSION); if (!g_pD3D) return; //检测硬件设备能力的方法 /*D3DCAPS9 caps; ZeroMemory(&caps, sizeof(caps)); g_pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps);*/ //获得相关信息,屏幕大小,像素点属性 D3DDISPLAYMODE d3ddm; ZeroMemory(&d3ddm, sizeof(d3ddm)); g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm); //设置全屏模式 D3DPRESENT_PARAMETERS d3dpp; ZeroMemory(&d3dpp, sizeof(d3dpp)); /*d3dpp.Windowed = false; d3dpp.BackBufferWidth = d3ddm.Width; d3dpp.BackBufferHeight = d3ddm.Height;*/ d3dpp.Windowed = true; d3dpp.BackBufferFormat = d3ddm.Format; d3dpp.BackBufferCount = 1; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;//交换后原缓冲区数据丢弃 //是否开启自动深度模板缓冲 d3dpp.EnableAutoDepthStencil = true; //当前自动深度模板缓冲的格式 d3dpp.AutoDepthStencilFormat = D3DFMT_D16;//每个像素点有16位的存储空间,存储离摄像机的距离 g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pDevice); if (!g_pDevice) return; //设置渲染状态,设置启用深度值 g_pDevice->SetRenderState(D3DRS_ZENABLE, true); //设置渲染状态,关闭灯光 g_pDevice->SetRenderState(D3DRS_LIGHTING, false); } void onInit() { //初始化D3D onCreatD3D(); //初始化顶点 initVB(); } void onDestroy() { if (!g_pDevice) g_pDevice->Release(); g_pDevice = NULL; } void onLogic(float fElapsedTime) { } void onRender(float fElasedTime) { //前两个参数是0和NULL时,清空整个游戏窗口的内容(清的是后台) //第三个是清除的对象:前面表示清除颜色缓冲区,后面表示清除深度缓冲区,D3DCLEAR_STENCIL清空模板缓冲区 g_pDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,100,100), 1.0f, 0); g_pDevice->BeginScene(); //----------绘制图形步骤6.设置数据源,设置灵活顶点格式,绘制图元 //设置数据流来源 g_pDevice->SetStreamSource( 0, //数据流管道号(0-15) g_pVB, //数据来源 0, //数据流偏移量 sizeof(stVertex) //每个数据的字节数大小 ); //通知系统数据格式,以便解析数据 g_pDevice->SetFVF(D3DFVF_CUSTOMVERTEX); //绘制图元 g_pDevice->DrawPrimitive( D3DPT_TRIANGLELIST, //三角形列 0, //起始点编号 1 //图元数量 ); g_pDevice->EndScene(); g_pDevice->Present(NULL, NULL, NULL, NULL); } int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // TODO: 在此放置代码。 MSG msg; HACCEL hAccelTable; // 初始化全局字符串 LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_D3DDEMO, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); // 执行应用程序初始化: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_D3DDEMO)); ZeroMemory(&msg, sizeof(msg)); while (msg.message != WM_QUIT) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else { static DWORD dwTime = timeGetTime(); DWORD dwCurrentTime = timeGetTime(); DWORD dwElapsedTime = dwCurrentTime - dwTime; float fElapsedTime = dwElapsedTime * 0.001f; //------------渲染和逻辑部分代码---------- onLogic(fElapsedTime); onRender(fElapsedTime); //----------------------------------------- if (dwElapsedTime < 1000 / 60) { Sleep(1000/ 60 - dwElapsedTime); } dwTime = dwCurrentTime; } } onDestroy(); return (int) msg.wParam; } // // 函数: MyRegisterClass() // // 目的: 注册窗口类。 // ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_D3DDEMO)); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = MAKEINTRESOURCE(IDC_D3DDEMO); wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); return RegisterClassEx(&wcex); } // // 函数: InitInstance(HINSTANCE, int) // // 目的: 保存实例句柄并创建主窗口 // // 注释: // // 在此函数中,我们在全局变量中保存实例句柄并 // 创建和显示主程序窗口。 // BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { hInst = hInstance; // 将实例句柄存储在全局变量中 g_hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!g_hWnd) { return FALSE; } SetMenu(g_hWnd, NULL); ShowWindow(g_hWnd, nCmdShow); UpdateWindow(g_hWnd); onInit(); return TRUE; } // // 函数: WndProc(HWND, UINT, WPARAM, LPARAM) // // 目的: 处理主窗口的消息。 // // WM_COMMAND - 处理应用程序菜单 // WM_PAINT - 绘制主窗口 // WM_DESTROY - 发送退出消息并返回 // // LRESULT CALLBACK WndProc(HWND g_hWnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; switch (message) { case WM_KEYDOWN: if (wParam == VK_ESCAPE) PostQuitMessage(0); break; case WM_CLOSE: DestroyWindow(g_hWnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(g_hWnd, message, wParam, lParam); } return 0; }
关于顶点格式的问题(D3DFVF_XYZ和D3DFVF_XYZRHW顶点格式的区别):
在上面的例子中使用的是x,y,z,rhw的顶点格式,定义时为D3DFVF_XYZRHW。
DX中原文是这样写的:
The vertices in the Vertices sample project are transformed. In other words, they are already in 2D window coordinates. This means that the point (0,0) is at the top left corner and the positive x-axis is right and the positive y-axis is down. These vertices are also lit, meaning that they are not using Direct3D lighting but are supplying their own color.
正常情况下,我们使用的三维坐标,是xyz格式的,定义为D3DFVF_XYZ,这种类型的顶点格式,需要进行世界变换,投影变换,视图变换等才能转化到到我们窗口中。而D3DFVF_XYZRHW格式的顶点,是已经经过了这些处理,换句话说,这种顶点格式,直接就是我们屏幕上的坐标,因而对于三维的那些世界变换,取景变换对这种顶点格式无效。x表示横坐标,y表示纵坐标(屏幕左上角为原点,向右为x正方向,向下为y正方向),z坐标被忽略了(貌似关于fog会有用,不过这里我试验了一下,改变z坐标貌似没有什么影响),我们一般设置成0即可,而rhw表示投影空间中顶点所在的齐次点(x, y, z, w)的w坐标的倒数。
注意,D3DFVF_XYZ和D3DFVF_XYZRHW这两种顶点格式不能共存。