1. 程式人生 > >【 Visual C 】遊戲開發筆記之二 最簡單的DirectX vc視窗的編寫

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

               

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

Direct3D要呼叫很多函式才能成功設定API。一旦完成設定,並且設定成功,就可以向螢幕上渲染圖形。

下面是函式中設定Direct3D所需的最少程式碼。

bool InitializeD3D(HWND hWnd, bool fullscreen){D3DDISPLAYMODE displayMode;// Create the D3D object.g_D3D = Direct3DCreate9(D3D_SDK_VERSION);if(g_D3D == NULL
) return false;// Get the desktop display mode.if(FAILED(g_D3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode)))return false;// Set up the structure used to create the D3DDeviceD3DPRESENT_PARAMETERS d3dpp;ZeroMemory(&d3dpp, sizeof(d3dpp));if(fullscreen){d3dpp.Windowed = FALSE;d3dpp.BackBufferWidth = 640
;d3dpp.BackBufferHeight = 480;}elsed3dpp.Windowed = TRUE;d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;d3dpp.BackBufferFormat = displayMode.Format;// Create the D3DDeviceif(FAILED(g_D3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_D3DDevice))){return
false;}return true;}

上段程式碼中的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結構體的定義:
typedef struct _D3DPRESENT_PARAMETERS_ {UINT BackBufferWidth; //視窗寬度UINT BackBufferHeight; //視窗高度D3DFORMAT BackBufferFormat; //渲染後臺快取的格式UINT BackBufferCount; //想要用於渲染的後臺快取總數D3DMULTISAMPLE_TYPE MultiSampleType;D3DSWAPEFFECT SwapEffect;HWND hDeviceWindow;BOOL Windowed;BOOL EnableAutoDepthStencil;D3DFORMAT AutoDepthStencilFormat;DWORD Flags;UINT FullScreen_RefreshRateInHz;UINT FullScreen_PresentationInterval;} 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()函式原型程式碼如下:

HRESULT CreateDevice( UINT Adapter,D3DDEVTYPE DeviceType,HWND hFocusWindow,DWORD BehaviorFlags,D3DPRESENT_PARAMETERS *pPresentationParameters,IDirect3DDevice9 **ppReturnedDeviceInterface);

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給出了驗證這個過程的例子程式碼。例子程式碼建立簡單的顯示空白黑屏的函式。

在螢幕上繪製一個空白屏的程式碼:
void RenderScene(){// Clear the backbuffer.g_D3DDevice->Clear(0, NULL, D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0), 1.0f, 0);// Begin the scene. Start rendering.g_D3DDevice->BeginScene();// End the scene. Stop rendering.g_D3DDevice->EndScene();// Display the scene.g_D3DDevice->Present(NULL, NULL, NULL, NULL);}

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物件的程式碼
void Shutdown(){if(g_D3DDevice != NULL) g_D3DDevice->Release();if(g_D3D != NULL) g_D3D->Release();g_D3DDevice = NULL;g_D3D = NULL;}

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

 完整的Blank Window演示程式程式碼如下:
#include<d3d9.h>#pragma comment(lib, "d3d9.lib")#pragma comment(lib, "d3dx9.lib")#define WINDOW_CLASS "UGPDX"#define WINDOW_NAME "Blank D3D Window"// Function Prototypes...bool InitializeD3D(HWND hWnd, bool fullscreen);void RenderScene();void Shutdown();// Direct3D object and device.LPDIRECT3D9 g_D3D = NULL;LPDIRECT3DDEVICE9 g_D3DDevice = NULL;LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){switch(msg){case WM_DESTROY:PostQuitMessage(0);return 0;break;case WM_KEYUP:if(wParam == VK_ESCAPE) PostQuitMessage(0);break;}return DefWindowProc(hWnd, msg, wParam, lParam);}int WINAPI WinMain(HINSTANCE hInst, HINSTANCE prevhInst, LPSTR cmdLine, int show){// Register the window classWNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,GetModuleHandle(NULL), NULL, NULL, NULL, NULL,WINDOW_CLASS, NULL };RegisterClassEx(&wc);// Create the application's windowHWND hWnd = CreateWindow(WINDOW_CLASS, WINDOW_NAME, WS_OVERLAPPEDWINDOW,100, 100, 640, 480, GetDesktopWindow(), NULL,wc.hInstance, NULL);// Initialize Direct3Dif(InitializeD3D(hWnd, false)){// Show the windowShowWindow(hWnd, SW_SHOWDEFAULT);UpdateWindow(hWnd);// Enter the message loopMSG msg;ZeroMemory(&msg, sizeof(msg));while(msg.message != WM_QUIT){if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)){TranslateMessage(&msg);DispatchMessage(&msg);}elseRenderScene();}}// Release any and all resources.Shutdown();// Unregister our window.UnregisterClass(WINDOW_CLASS, wc.hInstance);return 0;}bool InitializeD3D(HWND hWnd, bool fullscreen){D3DDISPLAYMODE displayMode;// Create the D3D object.g_D3D = Direct3DCreate9(D3D_SDK_VERSION);if(g_D3D == NULL) return false;// Get the desktop display mode.if(FAILED(g_D3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode)))return false;// Set up the structure used to create the D3DDeviceD3DPRESENT_PARAMETERS d3dpp;ZeroMemory(&d3dpp, sizeof(d3dpp));if(fullscreen){d3dpp.Windowed = FALSE;d3dpp.BackBufferWidth = 640;d3dpp.BackBufferHeight = 480;}elsed3dpp.Windowed = TRUE;d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;d3dpp.BackBufferFormat = displayMode.Format;// Create the D3DDeviceif(FAILED(g_D3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_D3DDevice))){return false;}return true;}void RenderScene(){// Clear the backbuffer.g_D3DDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0f, 0);// Begin the scene. Start rendering.g_D3DDevice->BeginScene();// End the scene. Stop rendering.g_D3DDevice->EndScene();// Display the scene.g_D3DDevice->Present(NULL, NULL, NULL, NULL);}void Shutdown(){if(g_D3DDevice != NULL) g_D3DDevice->Release();if(g_D3D != NULL) g_D3D->Release();}

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

我們先編譯該程式,然後執行它。我們可以看到一個640×480解析度的空白視窗。

下面給出了執行後的視窗截圖:

The end

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

淺墨歷時一年為遊戲程式設計愛好者鍛造的著作《逐夢旅程:Windows遊戲程式設計之從零開始》如果你喜歡淺墨寫的【Visual C++】遊戲開發系列部落格文章,那麼你一定會愛上這本書。這是淺墨專門為熱愛遊戲程式設計的朋友們寫的入門級遊戲程式設計寶典。噹噹網|京東商城|亞馬遜

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

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

淺墨歷時一年為遊戲程式設計愛好者鍛造的著作《逐夢旅程:Windows遊戲程式設計之從零開始》如果你喜歡淺墨寫的【Visual C++】遊戲開發系列部落格文章,那麼你一定會愛上這本書。這是淺墨專門為熱愛遊戲程式設計的朋友們寫的入門級遊戲程式設計寶典。噹噹網|京東商城|亞馬遜

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