【 Visual C++】遊戲開發筆記之二——最簡單的DirectX,vc窗口的編寫

筆記一中我們介紹瞭如何用代碼創建空的win32窗口,然而創建空的win32窗口只完成了一半的工作,接下來要做的工作是設置Direct3D,從而可以在屏幕上渲染圖形。



Direct3D要調用很多函數才能成功設置API。一旦完成設置,並且設置成功,就可以向屏幕上渲染圖形。

下面是函數中設置Direct3D所需的最少代碼。



  1. bool InitializeD3D(HWND hWnd, bool fullscreen)  
  2. {  
  3. D3DDISPLAYMODE displayMode;  
  4. // Create the D3D object.  
  5. g_D3D = Direct3DCreate9(D3D_SDK_VERSION);  
  6. if(g_D3D == NULL) return false;  
  7. // Get the desktop display mode.  
  8. if(FAILED(g_D3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode)))  
  9. return false;  
  10. // Set up the structure used to create the D3DDevice  
  11. D3DPRESENT_PARAMETERS d3dpp;  
  12. ZeroMemory(&d3dpp, sizeof(d3dpp));  
  13. if(fullscreen)  
  14. {  
  15. d3dpp.Windowed = FALSE;  
  16. d3dpp.BackBufferWidth = 640;  
  17. d3dpp.BackBufferHeight = 480;  
  18. }  
  19. else  
  20. d3dpp.Windowed = TRUE;  
  21. d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;  
  22. d3dpp.BackBufferFormat = displayMode.Format;  
  23. // Create the D3DDevice  
  24. if(FAILED(g_D3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,  
  25. D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_D3DDevice)))  
  26. {  
  27. return false;  
  28. }  
  29. return true;  
  30. }  



上段代碼中的InitializeD3D函數的參數有窗口句柄hWnd,標識窗口是否全屏的標識符fullscreen。窗口句柄是在調用CreateWindows()函數創建窗口句柄時,返回給WinMain()函數的數值。InitializeD3D()函數開始先調用Direct3DCreat9()函數。Direct3DCreat9()函數將創建一個Direct3D接口對象,並返回該對象。該函數所帶的參數值爲D3D_SDK_VERSION。如果從該函數創建的接口不爲NULL(空),那麼接口創建成功。如果是NULL,那麼創建接口時就會出錯。其他步驟很大程度上取決於是否成功調用Direct3DCreat9()函數,所以一旦出現錯誤,就會立刻退出Direct3D初始化程序。



接下來調用的是GetAdapterDisplayMode()
函數。該函數將返回當前的顯示信息,如桌面分辨率(寬度和高度),顯示格式,顯示器的刷新頻率等。該函數的參數包括正在查詢的適配器以及保存信息的顯示模式對象。將D3DADAPTER_DEFAULT發送給函數,詳細說明代碼,通過這些代碼可以獲取想要的主顯卡信息。

檢索顯卡信息之後,函數接下來會創建D3DPRESENT_PARAMETERS對象。D3DPRESENT_PARAMETERS結構用於定義Direct3D窗口的顯示信息。這樣可以設置正在創建窗口的期望寬度和高度,刷新率,顯示模式爲全屏或窗口,後天緩存數目等。



D3DPRESENT_PARAMETERS結構體的定義:


  1. typedef struct _D3DPRESENT_PARAMETERS_ {  
  2. UINT BackBufferWidth; //窗口寬度  
  3. UINT BackBufferHeight; //窗口高度  
  4. D3DFORMAT BackBufferFormat; //渲染後臺緩存的格式  
  5. UINT BackBufferCount; //想要用於渲染的後臺緩存總數  
  6. D3DMULTISAMPLE_TYPE MultiSampleType;  
  7. D3DSWAPEFFECT SwapEffect;  
  8. HWND hDeviceWindow;  
  9. BOOL Windowed;  
  10. BOOL EnableAutoDepthStencil;  
  11. D3DFORMAT AutoDepthStencilFormat;  
  12. DWORD Flags;  
  13. UINT FullScreen_RefreshRateInHz;  
  14. UINT FullScreen_PresentationInterval;  
  15. } D3DPRESENT_PARAMETERS;  


D3DPRESENT_PARAMETERS結構體是的前兩個變量是BackBufferWidth( 窗口寬度 ),BackBufferHeight(窗口高度)。接下來的變量BackBufferFormat代表渲染後臺緩存的格式。將D3DFMT_DEFAULT發送給該參數,就會得到所要用到的桌面格式。該參數可以接收的其他參數值分別是D3DFMT_A2R10G10B10(紅、綠、藍各10位,Alpha通道2位,總計32位),D3DFMT_A8R8G8B8(紅、綠、藍Alpha各8位,總計32位)、D3DFMT_X8R8G8B8、D3DFMT_AIR5G5B5、D3DFMT_XIR5G5B5和D3DFMT_R5G5B5。


BcakBufferCount是想要用於渲染的後臺緩存總數。通常,使用一個後臺緩存和一個主緩存,使用多個緩存會使動畫更流暢,因爲後臺緩存上繪製內容,然後將結果複製到主緩存,主緩存將其顯示在屏幕上。如果所有的事情都在主緩存處理,那麼遊戲玩家在玩遊戲時,會看到正在刷新的屏幕,從而會產生視覺假象,破壞遊
戲的視覺效果。成員變量MultiSampleType、MultiSampleQuality和SwapEffect處理交換效果。hDeviceWindow是調用WinMain()中的CreateWindow()函數創建的窗口句柄。Windowed標識符用於指定創建的窗口是否是全屏窗口。

EnableAutoDepthStencil標識符用於設置是否用Direct3D管理緩存深度以及模板緩存。AutoDepthStencilFormat將深度和模板緩存設置爲BackBufferFormat可以使用的相同值之一。


結構中的最後三個變量分別是Flags、FullScreen_RefreshRateInHz和PresentationInterval。Flags標識符可以是D3DPRESENTFLAG_DEVICECLIP、D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL和D3DPRESENTFLAG_LOCKABLE_ BUFFER或D3DPRESENTFLAG_VIDEO的任意組合。變量FullScreen_RefreshRateInHz存儲顯示器的刷新率。對窗口模式程序而言,該值必須爲0,但對全屏模式而言,該值取決於顯示器。對此,可以調用GetDisplayAdapter()函數獲取顯示模式。最後一個變量PresentationInterval處理可以顯示的交換鏈的後臺緩存的最大次數。交換鏈主要讓程序有多個窗口在桌面上同時顯示(每個渲染自己的窗口)本書不涉及交換鏈的討論,所以這裏將不使用該變量。程序清單1.3 InitializeD3D()函數中調用的最後一個函數是CreateDevice()。該函數主要負責創建Direct3D設備對象,該對象用於向屏幕渲染圖形。如果函數失敗,最後一個參數(對象被髮送給ppReturnedDeviceInterface)將爲NULL(空)。測試函數是否失敗的另一種方法是判斷函數的返回值是否是D3D_OK以外的其他值。如果不是D3D_OK,則意味着創建過程中出現了問題,而且不會按照給定的規格創建設備。下面的代碼給出了CreateDevice()的函數原型。


CreateDevice()函數原型代碼如下:


  1. HRESULT CreateDevice( UINT Adapter,  
  2. D3DDEVTYPE DeviceType,  
  3. HWND hFocusWindow,  
  4. DWORD BehaviorFlags,  
  5. D3DPRESENT_PARAMETERS *pPresentationParameters,  
  6. IDirect3DDevice9 **ppReturnedDeviceInterface  
  7. );  



CreateDevice()函數中的第一個參數是Adapter。該參數用於指定正在使用的顯卡。第二個參數DeviceType是一個標識符,用於指定Direct3D中的渲染方式。該參數的參數值可以是採用硬件渲染的D3DDEVTYPE_HAL、採用軟件渲染的D3DDEVTYPE_REF、不需要軟硬件支持的D3DDEVTYPE_NULLREF,或是採用要進行渲染工作的可插拔軟件的D3DDEVTYPE_SW。軟件渲染標識符允許運行Direct3D程序,它可以使用硬件不支持的特性。軟件渲染存在的問題是渲染速度慢,尤其是在開發遊戲時。

下一參數hFocusWindow也是一個窗口句柄。參數BehaviorFlags是標識符組合,用於指定設備的運行方式。


pPresentationParameter是一個指針,它指向該函數前面創建的D3DPRESENT_PARAMTERS結構。ppReturnedDeviceInterface是一個指針,它指向新創建的Direct3D設備對象。如果該對象爲NULL(空),或是函數返回除D3D_OK以外的值,那麼Direct3D設備的創建失敗。


一旦完成Direct3D的設置和創建,就可以隨意渲染屏幕。初始化階段的CreateDevice()函數中創建的設備對象可以完成Direct3D中的渲染工作。渲染屏幕開始先要清屏爲指定的顏色,告知Direct3D將要開始繪製新場景,渲染想要渲染的物體,完成屏幕渲染,在屏幕上顯示渲染結果。代碼1.6給出了驗證這個過程的例子代碼。例子代碼創建簡單的顯示空白黑屏的函數。


在屏幕上繪製一個空白屏的代碼:


  1. void RenderScene()  
  2. {  
  3. // Clear the backbuffer.  
  4. g_D3DDevice->Clear(0, NULL, D3DCLEAR_TARGET,  
  5. D3DCOLOR_XRGB(0,0,0), 1.0f, 0);  
  6. // Begin the scene. Start rendering.  
  7. g_D3DDevice->BeginScene();  
  8. // End the scene. Stop rendering.  
  9. g_D3DDevice->EndScene();  
  10. // Display the scene.  
  11. g_D3DDevice->Present(NULL, NULL, NULL, NULL);  
  12. }  




RenderScene()函數開始先清除後臺緩存。調用Direct3D設備對象的Clear()函數可以完成該工作。Clear()函數的參數包括:要清除的矩形數目(0代表清除整個屏幕)、定義想要清除的屏幕區域矩形鏈表(NULL代表整個屏幕)、標識要清除內容的標識符、清除後的顏色、要設定的深度值、設定模板緩存值。對標識符參數而言,D3DCLEAR_DEFAULT清除所有的渲染目標,D3DCLEAR_STENCIL清除模板緩存,而D3DCLEAR_ZBUFFER清除深度緩存。後面將會更多地介紹深度緩存。


一旦完成清除工作,渲染函數將調用設備對象的BeginScene()函數,在Direct3D中啓動一個新場景。在Direct3D中渲染任何圖形前,都必須先調用BeginScene()函數。一旦渲染完要渲染的物體,就調用設備對象的EndScene()函數結束渲染。記住:每個BeginScene()函數必須有一個與之對應的EndScene()函數。由於這裏只是繪製了一個空白窗口,所以在BeginScene()和EndScene()之間不需要添加任何代碼。


最後一步是在屏幕上顯示渲染結果。調用設備對象的Present()函數可以完成顯示。就目前的學習而言,Present()函數的參數可以全部設爲NULL(空)。第一個參數是正在顯示的原始矩形,如果不使用交換鏈,那麼該值必須爲NULL(空)。第二個參數是一個指針,它指向要渲染的最終矩形。第三個參數是正在顯示的窗口的窗口句柄。另外,由於沒有用到交換鏈,所以對正在使用的窗口句柄而言,該值設爲NULL(空)。這個正在使用的窗口句柄是在Direct3D初始化過程中爲D3DPRESENT_PARAMETERS對象設置的窗口句柄。Present()函數中的最後一個參數是緩存區域,它代表需要更新的最小區域。同樣,該參數涉及到交換鏈,也可以設爲NULL(空)。


既然可以使用Direct3D初始化和渲染屏幕,那麼剩下要做的工作是在退出程序時,釋放對象。在Direct3D中,會發現通常要在退出程序前釋放系統使用的內存,從而避免內存泄漏。所有要釋放的內存都可以通過調用對象的Release()函數釋放。Release()函數會減少對象的引用計數。如果引用計數降爲0,那麼系統就可以安全地從內存中刪除對象。如果對單個對象有多個引用,那麼必須牢記對所有的對象都要調用Release()函數,否則將不會釋放內存。要牢記的規則就是:如果創建了對象,那麼最終就要釋放該對象。下述代碼給出了在一個名爲Shutdown()的函數中釋放兩個Direct3D對象的方法。


釋放所有的Direct3D對象的代碼

  1. void Shutdown()  
  2. {  
  3. if(g_D3DDevice != NULL) g_D3DDevice->Release();  
  4. if(g_D3D != NULL) g_D3D->Release();  
  5. g_D3DDevice = NULL;  
  6. g_D3D = NULL;  
  7. }  


到這裏,基本的模塊都已經一目瞭然了,下面我們把他串聯起來,構成一個整體。

 完整的Blank Window演示程序代碼如下:

  1. #include<d3d9.h>  
  2. #pragma comment(lib, "d3d9.lib")  
  3. #pragma comment(lib, "d3dx9.lib")  
  4. #define WINDOW_CLASS "UGPDX"  
  5. #define WINDOW_NAME "Blank D3D Window"  
  6. // Function Prototypes...  
  7. bool InitializeD3D(HWND hWnd, bool fullscreen);  
  8. void RenderScene();  
  9. void Shutdown();  
  10. // Direct3D object and device.  
  11. LPDIRECT3D9 g_D3D = NULL;  
  12. LPDIRECT3DDEVICE9 g_D3DDevice = NULL;  
  13. LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)  
  14. {  
  15. switch(msg)  
  16. {  
  17. case WM_DESTROY:  
  18. PostQuitMessage(0);  
  19. return 0;  
  20. break;  
  21. case WM_KEYUP:  
  22. if(wParam == VK_ESCAPE) PostQuitMessage(0);  
  23. break;  
  24. }  
  25. return DefWindowProc(hWnd, msg, wParam, lParam);  
  26. }  
  27. int WINAPI WinMain(HINSTANCE hInst, HINSTANCE prevhInst, LPSTR cmdLine, int show)  
  28. {  
  29. // Register the window class  
  30. WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,  
  31. GetModuleHandle(NULL), NULL, NULL, NULL, NULL,  
  32. WINDOW_CLASS, NULL };  
  33. RegisterClassEx(&wc);  
  34. // Create the application's window  
  35. HWND hWnd = CreateWindow(WINDOW_CLASS, WINDOW_NAME, WS_OVERLAPPEDWINDOW,  
  36. 100, 100, 640, 480, GetDesktopWindow(), NULL,  
  37. wc.hInstance, NULL);  
  38. // Initialize Direct3D  
  39. if(InitializeD3D(hWnd, false))  
  40. {  
  41. // Show the window  
  42. ShowWindow(hWnd, SW_SHOWDEFAULT);  
  43. UpdateWindow(hWnd);  
  44. // Enter the message loop  
  45. MSG msg;  
  46. ZeroMemory(&msg, sizeof(msg));  
  47. while(msg.message != WM_QUIT)  
  48. {  
  49. if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))  
  50. {  
  51. TranslateMessage(&msg);  
  52. DispatchMessage(&msg);  
  53. }  
  54. else  
  55. RenderScene();  
  56. }  
  57. }  
  58. // Release any and all resources.  
  59. Shutdown();  
  60. // Unregister our window.  
  61. UnregisterClass(WINDOW_CLASS, wc.hInstance);  
  62. return 0;  
  63. }  
  64. bool InitializeD3D(HWND hWnd, bool fullscreen)  
  65. {  
  66. D3DDISPLAYMODE displayMode;  
  67. // Create the D3D object.  
  68. g_D3D = Direct3DCreate9(D3D_SDK_VERSION);  
  69. if(g_D3D == NULL) return false;  
  70. // Get the desktop display mode.  
  71. if(FAILED(g_D3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode)))  
  72. return false;  
  73. // Set up the structure used to create the D3DDevice  
  74. D3DPRESENT_PARAMETERS d3dpp;  
  75. ZeroMemory(&d3dpp, sizeof(d3dpp));  
  76. if(fullscreen)  
  77. {  
  78. d3dpp.Windowed = FALSE;  
  79. d3dpp.BackBufferWidth = 640;  
  80. d3dpp.BackBufferHeight = 480;  
  81. }  
  82. else  
  83. d3dpp.Windowed = TRUE;  
  84. d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;  
  85. d3dpp.BackBufferFormat = displayMode.Format;  
  86. // Create the D3DDevice  
  87. if(FAILED(g_D3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,  
  88. D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_D3DDevice)))  
  89. {  
  90. return false;  
  91. }  
  92. return true;  
  93. }  
  94. void RenderScene()  
  95. {  
  96. // Clear the backbuffer.  
  97. g_D3DDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0f, 0);  
  98. // Begin the scene. Start rendering.  
  99. g_D3DDevice->BeginScene();  
  100. // End the scene. Stop rendering.  
  101. g_D3DDevice->EndScene();  
  102. // Display the scene.  
  103. g_D3DDevice->Present(NULL, NULL, NULL, NULL);  
  104. }  
  105. void Shutdown()  
  106. {  
  107. if(g_D3DDevice != NULL) g_D3DDevice->Release();  
  108. if(g_D3D != NULL) g_D3D->Release();  
  109. }  




只要成功安裝了DirectX SDK,並配置好DirectX的開發環境,則萬事俱備。

我們先編譯該程序,然後運行它。我們可以看到一個640×480分辨率的空白窗口。

下面給出了運行後的窗口截圖:





以上就是本節筆記的全部內容。

本節源代碼請點擊這裏下載:【Visual C++】Code_Note_2


The end



------------------------------------------------------------------------------------------------------------------------------

淺墨歷時一年爲遊戲編程愛好者鍛造的著作《逐夢旅程:Windows遊戲編程之從零開始》

如果你喜歡淺墨寫的【Visual C++】遊戲開發系列博客文章,那麼你一定會愛上這本書。

這是淺墨專門爲熱愛遊戲編程的朋友們寫的入門級遊戲編程寶典。



------------------------------------------------------------------------------------------------------------------------------


------------------------------------------------------------------------------------------------------------------------------

淺墨歷時一年爲遊戲編程愛好者鍛造的著作《逐夢旅程:Windows遊戲編程之從零開始》

如果你喜歡淺墨寫的【Visual C++】遊戲開發系列博客文章,那麼你一定會愛上這本書。

這是淺墨專門爲熱愛遊戲編程的朋友們寫的入門級遊戲編程寶典。



------------------------------------------------------------------------------------------------------------------------------

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章