今天在学习DirectX时发现了几个问题:
1、灵活顶点格式(FVF)的取值到底对图像的呈现有什么影响?
2、顶点的数据格式是否可以自己设置?
3、D3DXMatrixPerspectiveFovRH 和 D3DXMatrixPerspectiveFovLH有什么区别?
先贴出我的测试代码:
1 /* 2 运行所需的库文件:d3dx9d.lib,d3d9.lib,d3dx11.lib,dxerr.lib,dxguid.lib,winmm.lib,comctl32.lib 3 */ 4 5 #include <windows.h> 6 #include <windowsx.h> 7 #include <d3d9.h> 8 #include <D3DX10Math.h> 9 10 #define WINDOW_WIDTH 600 11 #define WINDOW_HEIGHT 800 12 #define WINDOW_CLASS_NAME TEXT("C_class") 13 #define SAFE_RELEASE(p) {if(p){(p)->Release();(p) = NULL;}} 14 15 #pragma comment(lib,"winmm.lib") 16 #pragma comment(lib,"d3d9.lib") 17 #pragma comment(lib,"d3dx9d.lib") 18 19 //结构定义 20 struct MVertex { 21 MVertex() {}; 22 MVertex(float x, float y, float z) { 23 m_x = x; m_y = y; m_z = z; 24 } 25 float m_x, m_y, m_z; 26 static const DWORD FVF; 27 }; 28 const DWORD MVertex::FVF = D3DFVF_XYZ; 29 30 //全局声明 31 float time = 2.0f; 32 HWND g_hdc = NULL; 33 HINSTANCE g_hinstance = NULL; 34 IDirect3DDevice9* g_device = NULL; 35 IDirect3DVertexBuffer9* Vbuffer = NULL; 36 typedef LRESULT(CALLBACK* WNDPROCRET)(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 37 38 //全局函数 39 LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 40 WNDCLASSEX& gCreateWindowsClass(HINSTANCE hInstance, LPCTSTR name, WNDPROCRET lpfnproc,UINT style = CS_HREDRAW | CS_VREDRAW); 41 VOID gMessageLoop(); 42 HRESULT gGame_Init(HWND hwnd); 43 VOID gRender(HWND hwnd); 44 VOID Clean_Up(HWND hwnd); 45 VOID gCreateInterface_D3D(LPDIRECT3D9& pd3d); 46 VOID gGetHalinfo(LPDIRECT3D9 pd3d,int& vp); 47 VOID gFullParameter(HWND hwnd,D3DPRESENT_PARAMETERS& d3dpp); 48 VOID gCreateVertexBuffer(IDirect3DDevice9* pdevice); 49 VOID gCreatecamera(IDirect3DDevice9* pdevice, D3DXVECTOR3& pos, D3DXVECTOR3& target, D3DXVECTOR3& up); 50 VOID gTranslation(IDirect3DDevice9* pdevice); 51 VOID gMydraw(float time, IDirect3DDevice9* pdevice); 52 53 54 int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) 55 { 56 g_hinstance = hInstance; 57 58 WNDCLASSEX WClass = gCreateWindowsClass(hInstance, WINDOW_CLASS_NAME, WindowProc); 59 60 if (!RegisterClassEx(&WClass)) 61 { 62 MessageBox(0, TEXT("error"), TEXT("注册失败!"), MB_OK); 63 return 0; 64 } 65 66 g_hdc = CreateWindow(WINDOW_CLASS_NAME, TEXT("CYX"), WS_OVERLAPPEDWINDOW | WS_EX_TOPMOST,0,0,WINDOW_WIDTH,WINDOW_HEIGHT,0,0,hInstance,0); 67 if (!g_hdc) 68 { 69 MessageBox(0, TEXT("error"), TEXT("窗口创建失败!"), MB_OK); 70 return 0; 71 } 72 73 ShowWindow(g_hdc, nShowCmd); 74 UpdateWindow(g_hdc); 75 76 if (FAILED(gGame_Init(g_hdc))) { 77 MessageBox(0, TEXT("error"), TEXT("游戏初始化失败!"), MB_OK); 78 return 0; 79 } 80 81 gMessageLoop(); 82 83 UnregisterClass(WINDOW_CLASS_NAME, hInstance); 84 return 0; 85 } 86 87 //函数定义 88 LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { 89 switch (uMsg) { 90 case WM_PAINT: 91 BeginPaint(hwnd, NULL); 92 gRender(hwnd); 93 //ValidateRect(hwnd, NULL);//这个函数还不是很清楚 94 EndPaint(hwnd, NULL); 95 break; 96 case WM_DESTROY: 97 Clean_Up(hwnd); 98 PostQuitMessage(0); 99 break; 100 case WM_KEYDOWN: 101 switch (wParam) { 102 case VK_ESCAPE: 103 DestroyWindow(hwnd); 104 break; 105 case VK_UP: 106 time += 5.0f; 107 break; 108 case VK_DOWN: 109 time -= 5.0f; 110 break; 111 default:break; 112 } 113 default: 114 break; 115 } 116 return DefWindowProc(hwnd, uMsg, wParam, lParam); 117 } 118 119 WNDCLASSEX& gCreateWindowsClass(HINSTANCE hInstance,LPCTSTR name, WNDPROCRET lpfnproc, UINT style /* = CS_HREDRAW | CS_VREDRAW */) { 120 121 static WNDCLASSEX Mywindowclass; 122 Mywindowclass.cbSize = sizeof(Mywindowclass); 123 Mywindowclass.cbClsExtra = 0; 124 Mywindowclass.cbWndExtra = 0; 125 Mywindowclass.lpfnWndProc = lpfnproc; 126 Mywindowclass.hInstance = hInstance; 127 Mywindowclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); 128 Mywindowclass.hCursor = LoadCursor(0, IDC_ARROW); 129 Mywindowclass.hIconSm = NULL; 130 Mywindowclass.hIcon = NULL; 131 Mywindowclass.lpszClassName = WINDOW_CLASS_NAME; 132 Mywindowclass.lpszMenuName = NULL; 133 Mywindowclass.style = style; 134 135 return Mywindowclass; 136 } 137 138 VOID gMessageLoop() { 139 MSG mmsg = { 0 }; 140 while (mmsg.message != WM_QUIT) 141 { 142 if (PeekMessage(&mmsg, 0, 0, 0, PM_REMOVE)) 143 { 144 TranslateMessage(&mmsg); 145 DispatchMessage(&mmsg); 146 } 147 else 148 { 149 gRender(g_hdc); 150 } 151 } 152 } 153 154 HRESULT gGame_Init(HWND hwnd) { 155 LPDIRECT3D9 pd3d = NULL; 156 gCreateInterface_D3D(pd3d); 157 158 int vp = 0; 159 gGetHalinfo(pd3d,vp); 160 161 D3DPRESENT_PARAMETERS d3dpp; 162 gFullParameter(hwnd, d3dpp); 163 164 if (FAILED(pd3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, vp, &d3dpp, &g_device))) 165 { 166 MessageBox(0, TEXT("error"), TEXT("设备创建失败!"), MB_OK); 167 return E_FAIL; 168 } 169 170 SAFE_RELEASE(pd3d); 171 172 //创建定点缓存 173 gCreateVertexBuffer(g_device); 174 175 //设置摄像机 176 D3DXVECTOR3 pos(.0f, 5.0f, 10.0f); 177 D3DXVECTOR3 tar(0.0f, 0.0f, 0.0f); 178 D3DXVECTOR3 up(0.0f, 1.0f, 0.0f); 179 gCreatecamera(g_device,pos,tar,up); 180 181 //变换 182 gTranslation(g_device); 183 184 return S_OK; 185 } 186 187 VOID gRender(HWND hwnd) { 188 gMydraw(time, g_device); 189 } 190 191 VOID Clean_Up(HWND hwnd) { 192 SAFE_RELEASE(g_device); 193 SAFE_RELEASE(Vbuffer); 194 } 195 196 VOID gCreateInterface_D3D(LPDIRECT3D9& pd3d) { 197 if (!(pd3d = Direct3DCreate9(D3D_SDK_VERSION))) 198 { 199 MessageBox(0, TEXT("error"), TEXT("接口创建失败!"), MB_OK); 200 return; 201 } 202 } 203 204 VOID gGetHalinfo(LPDIRECT3D9 pd3d,int& vp) { 205 D3DCAPS9 caps; 206 if (FAILED(pd3d->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps))) 207 { 208 MessageBox(0, TEXT("error"), TEXT("获取设备信息失败!"), MB_OK); 209 return; 210 } 211 if (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) 212 { 213 vp = D3DCREATE_HARDWARE_VERTEXPROCESSING; 214 } 215 else 216 vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING; 217 } 218 219 VOID gFullParameter(HWND hwnd,D3DPRESENT_PARAMETERS& d3dpp) { 220 ZeroMemory(&d3dpp, sizeof(d3dpp)); 221 d3dpp.BackBufferWidth = WINDOW_WIDTH; 222 d3dpp.BackBufferHeight = WINDOW_HEIGHT; 223 d3dpp.BackBufferCount = 1; 224 d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8; 225 d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8; 226 d3dpp.EnableAutoDepthStencil = TRUE; 227 d3dpp.Flags = 0; 228 d3dpp.hDeviceWindow = hwnd; 229 d3dpp.FullScreen_RefreshRateInHz = 0; 230 d3dpp.MultiSampleQuality = 0; 231 d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE; 232 d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; 233 d3dpp.Windowed = TRUE; 234 d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;//该参数是干什么的?? 235 } 236 237 VOID gCreateVertexBuffer(IDirect3DDevice9* pdevice) { 238 if (FAILED(pdevice->CreateVertexBuffer( 239 24 * sizeof(MVertex), 240 0, 241 MVertex::FVF, 242 D3DPOOL_MANAGED, 243 &Vbuffer, 244 0 245 ))) 246 { 247 MessageBox(0, TEXT("定点缓存创建失败!"), TEXT("error"), MB_OK); 248 return; 249 } 250 251 //填充定点缓存 252 MVertex* v = NULL; 253 Vbuffer->Lock(0, 0, (VOID**)&v, 0); 254 255 v[0] = MVertex(0.0f, 1.4f, 0.0f); v[6] = MVertex(0.0f,1.4f, 0.0f); 256 v[1] = MVertex(-1.0f, 0.0f, -1.0f); v[7] = MVertex(1.0f, 0.0f, 1.0f); 257 v[2] = MVertex(-1.0f, 0.0f, 1.0f); v[8] = MVertex(1.0f, 0.0f, -1.0f); 258 259 v[3] = MVertex(0.0f, 1.4f, 0.0f); v[9] = MVertex(0.0f, 1.4f, 0.0f); 260 v[4] = MVertex(-1.0f, 0.0f, 1.0f); v[10] = MVertex(1.0f, 0.0f, -1.0f); 261 v[5] = MVertex(1.0f, 0.0f, 1.0f); v[11] = MVertex(-1.0f, 0.0f, -1.0f); 262 263 v[12] = MVertex(0.0f, -1.4f, 0.0f); v[13] = MVertex(-1.0f, 0.0f, -1.0f); 264 v[14] = MVertex(1.0f, 0.0f, -1.0f); v[15] = MVertex(0.0f, -1.4f, 0.0f); 265 v[16] = MVertex(1.0f, 0.0f, -1.0f); v[17] = MVertex(1.0f, 0.0f, 1.0f); 266 267 v[18] = MVertex(0.0f, -1.4f, 0.0f); v[19] = MVertex(1.0f, 0.0f, 1.0f); 268 v[20] = MVertex(-1.0f, 0.0f, 1.0f); v[21] = MVertex(0.0f, -1.4f, 0.0f); 269 v[22] = MVertex(-1.0f, 0.0f, 1.0f); v[23] = MVertex(-1.0f, 0.0f, -1.0f); 270 271 Vbuffer->Unlock(); 272 } 273 274 VOID gCreatecamera(IDirect3DDevice9* pdevice, D3DXVECTOR3& pos, D3DXVECTOR3& target, D3DXVECTOR3& up) { 275 D3DXMATRIX V; 276 D3DXMatrixLookAtLH(&V, &pos, &target, &up); 277 pdevice->SetTransform(D3DTS_VIEW, &V); 278 } 279 280 VOID gTranslation(IDirect3DDevice9* pdevice) { 281 //投影变换 282 D3DXMATRIX proj; 283 //居然还有D3DMatrixPerspectiveFovRH() 284 D3DXMatrixPerspectiveFovLH( 285 &proj, 286 D3DX_PI*0.5f, 287 (float)WINDOW_WIDTH / (float)WINDOW_HEIGHT, 288 1.0f, 289 1000.0f 290 ); 291 pdevice->SetTransform(D3DTS_PROJECTION, &proj); 292 293 //设置线型 294 pdevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); 295 //打开光照 296 //pdevice->SetRenderState(D3DRS_LIGHTING, FALSE); 297 } 298 299 VOID gMydraw(float time, IDirect3DDevice9* pdevice) { 300 if (pdevice) 301 { 302 D3DXMATRIX Rx; 303 static float y = 0.0f; 304 D3DXMatrixRotationX(&Rx, y); 305 y += time*0.002; 306 if (y > 6.28f) 307 y = 0.0f; 308 pdevice->SetTransform(D3DTS_WORLD, &Rx); 309 310 pdevice->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(230, 230, 230), 1.0f, 0); 311 pdevice->BeginScene(); 312 pdevice->SetStreamSource(0, Vbuffer, 0,sizeof(MVertex)); 313 pdevice->SetFVF(MVertex::FVF); 314 pdevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 8); 315 pdevice->EndScene(); 316 pdevice->Present(0, 0, 0, 0); 317 } 318 }
现在我们来解决问题吧、
一、灵活顶点格式(FVF)的取值到底对图像的呈现有什么影响?
首先,常用的灵活顶点格式的取值及含义如下:
D3DFVF_XYZ——顶点的三维坐标(分别代表X,Y,Z坐标),表示有位置,而且需要进行矩阵变换(世界,投影,视口,取景)和设置摄像机位置才能在屏幕上显示;
D3DFVF_XYZRHW——即平时所说rhw,它说明顶点有位置,而且经过了矩阵变换,不用再由Direct3D对它进行变换,通常用于做UI(用户界面);
D3DFVF_DIFFUSE——表示顶点格式中有漫反射颜色(这涉及到顶点数据格式的设置),同时,必须开启了光照才能看到设置的颜色,否则是本色(一般是白色)的;
D3DFVF_NORMAL——表示顶点有法线向量;
D3DFVF_TEX*——表示顶点有纹理坐标,*可以是1至8,表示有多少套纹理坐标
举例来说,上述测试代码中,我的想法是将顶点格式设置为包含三个原始坐标的类,所以在FVF设置时我就将其设置为D3DFF_XYZ,同时,在后续绘制于屏幕之前,我将
我的坐标进行了投影变换同时设置了摄像机的位置,如果将摄像机注释掉,则在屏幕上看不到任何东西。
*注意:
1、D3DFVF_XYZ默认的用户区中心坐标是(0,0),而D3DFVF_XYZRHW是左上角为(0,0)
用D3DFVF_XYZ默认的为非光照的,而D3DFVF_XYZRHW是高洛德光照。
二、顶点的数据格式是否可以自己设置?
答案当然是一定的,不过一般要根据自己的FVF需求去设置,比如,如果你使用D3DFVF_XYZ(D3DFVF_XYZRHW) | D3DFVF_DIFFUSE | D3DFVF_NORMAL,那么你的顶点数据结构
就应该有三种内容:坐标,颜色,法向量。对于我而言,我习惯于将FVF也定义到顶点数据结构中,这比较方便、
三、D3DXMatrixPerspectiveFovRH 和 D3DXMatrixPerspectiveFovLH有什么区别?
前者是创建右手坐标透视投影矩阵,而后者是创建左手坐标透视投影矩阵。回顾一下左手坐标与右手坐标:
左手坐标:伸出左手,让拇指和食指成“L”形,大拇指向右,食指向上。其余的手指指向前方。这样就建立了一个左手坐标系。拇指、食指和其余手指分别代表x,y,z轴的正方向。判断方法:在空间直角坐标系中,让左手拇指指向x轴的正方向,食指指向y轴的正方向,如果中指能指向z轴的正方向,则称这个坐标系为左手直角坐标系.反之则是右手直角坐标系。
右手坐标:右手坐标系在我们以前初中高中学几何的时候也经常用到。在三维坐标系中,Z轴的正轴方向是根据右手定则确定的。右手定则也决定三维空间中任一坐标轴的正旋转方向。要标注X、Y和Z轴的正轴方向,就将右手背对着屏幕放置,拇指即指向X轴的正方向。伸出食指和中指,如右图所示,食指指向Y轴的正方向,中指所指示的方向即是Z轴的正方向。要确定轴的正旋转方向,如下图所示,用右手的大拇指指向轴的正方向,弯曲手指。那么手指所指示的方向即是轴的正旋转方向。