天天看點

DirectX 9.0c遊戲開發手記之RPG程式設計自學日志之6: Drawing with DirectX Graphics (用DirectX圖形繪圖)(第2節)

        本文由哈利_蜘蛛俠原創,轉載請注明出處!有問題請聯系[email protected]

        上一期我們講了第1節,但是純粹是理論。這一節我們要講理論聯系實際了!

        原文翻譯:

===============================================================================

2.2 Getting Started with DirectX Graphics (準備使用DirectX Graphics)

        現在你已經熟悉了繪制3-D圖像的基本知識了,那麼是時候開始将知識應用于實際了。但是,在你出發之前,你得了解怎麼準備圖形系統以供你使用。

        在本節中,我将向你介紹我在整本書中使用的DirectX Graphics元件,并且如何讓圖形系統運作起來并準備好繪制。

        注意:

===============================================================================

    雖然新的圖形元件叫做DirectX Graphics,我通常稱之為Direct3D,這是因為所有的3-D圖形都使用它。

===============================================================================

        注意:

===============================================================================

    為了在你的項目中開始使用Direct3D,請包含D3D9.H以及連結D3D9.LIB庫(實際上二者都是小寫)。

===============================================================================

2.2.1 Direct3D Components (Direct3D元件)

        Direct3D将圖形功能分割到了好幾個不同的COM對象之中。每一個對象都有其自己的目的,例如IDirect3D9對象是用來控制整個的圖形系統的,而IDirect3DDevice9對象是用來控制圖形是如何渲染到顯示屏上的。

        在本書中,我隻會給你們展示列在表2.1中的對象;這些對象是你在你的遊戲開發項目中最有可能用到的。

表2.1 主要的Direct3D元件

元件 描述
IDirect3D9 使用該對象來收集圖形硬體的資訊,并建立裝置接口。
IDirect3DDevice9 直接與3-D硬體打交道。使用它,你可以渲染圖形、操控圖像資源、建立并設定渲染狀态和着色過濾器(shade filters),等等等等。
IDirect3DVertexBuffer9 包含了一個頂點資訊的數組,用于繪制多邊形。
IDirect3DTexture9 使用該對象來存儲所有用于繪制3-D(以及2-D)圖形的面的圖像。

        注意:

===============================================================================

        雖然還有另外一些Direct3D元件可以處理,但是它們對于此書而言已經超出範圍了(并且是無用的)。你可以查找DirectX SDK文檔(對于DirectX June 2010來說,是windows_graphics這個.chm格式檔案)來擷取關于這些另外的對象的更多資訊。

===============================================================================

2.2.2 Initializingthe System (初始化系統)

        開始使用圖形系統是很簡單的工作,這得歸功于Direct3D的簡化。下面是建立以及運作圖形系統通常所需的四個步驟:

1、  擷取Direct3D的一個接口;

2、  選擇一個顯示模式(display mode);

3、  設定顯示方法(presentation method)。

4、  建立裝置接口并初始化顯示屏。

        這真是一張簡單明了的清單!我說過了,建立并運作圖形系統是一件簡單的事情,那麼我們就繼續來研究如何處理每一步吧!

2.2.3 Obtaining theDirect3D Interface (擷取Direct3D接口)

        使用圖形的第一步是初始化一個IDirect3D9對象。Direct3DCreate9函數為你做到這件事。

IDirect3D9 *Direct3DCreate9(
UINT SDKVersion);   //D3D_SDK_VERSION
           

        注意:

===============================================================================

        絕大多數DirectX函數(以及所有的COM對象的函數)傳回一個HRESULT值。時不時地,你會發現一些函數(例如Direct3DCreate9)傳回一個非HRESULT值,是以要仔細觀察!

===============================================================================

        這個函數的唯一一個參數應該是D3D_SDK_VERSION,它表示你正在使用的SDK的版本。該函數的傳回值是一個指向你所需要的新建立的IDirec3D9對象的指針,或者如果在建立Direct3D接口的時候發生錯誤的話,那麼傳回值是NULL。

        使用這個函數就兩步,第一步是執行個體化一個IDirec3D9對象,第二步是調用這個建立函數:

IDirect3D9 g_D3D; // global IDirect3D9 object
if((g_D3D =Direct3DCreate9(D3D_SDK_VERSION)) == NULL){
         //Error occurred
}
 
           

2.2.4 Selecting aDisplay Mode (選擇一個顯示模式)

        在IDirect3D9對象建立完畢之後,你可以開始向它詢問關于圖形系統的資訊了,包括Direct3D可以操控的顯示模式。事實上,你還可以向IDirect3D9對象詢問目前的顯示模式(如果你想使用那種格式的話)。

        顯示模式範圍其維數(以像素數表示的寬度和高度)、顔色深度(可以顯示的顔色數目)以及重新整理率。例如,你也許像使用640×480的分辨率、16位的顔色深度以及擴充卡預設的重新整理率。

        該顯示模式資訊儲存在一個D3DDISPLAYMODE結構體之中:

typedef struct _D3DDISPLAYMODE {
         UINT         Width;             //Screen width in pixels
         UINT         Height;            // Screen height in pixels
         UINT         RefreshRate;       // Refresh rate (0=default)
         D3DFORMAT    Format;            // Color format
} D3DDISPLAYMODE;
           

        你可以領會寬度、高度以及重新整理率,但是顔色格式呢?在圖形中,你通常可以選擇每個像素為了存儲顔色資訊而使用的位數(16位,24位或32位)。你使用的位數越多,你就可以顯示出更多的顔色(并且耗費的記憶體也就越多)。

        你通常用每一個顔色成分(紅、綠、藍,有時候還有alpha)所占據的位數來指代顔色模式。例如,如果我想要一個16位的顔色模式——紅色占5位,綠色占5位,藍色占5位,還有1位來儲存alpha值。每個顔色成分使用5位的位置可以使用32種明暗值(shades)。alpha值隻有1位,以為着它要麼是開的,要麼是關閉的。

        當你稱呼顔色模式的時候,你并不說16位之類的話,而是說每一個顔色成分所占的位數,例如1555(1位alpha,5位紅色,5位綠色,5位藍色)。标準的顔色模式為555(5位紅色,5位綠色,5位藍色),565(5位紅色,6位綠色,5位藍色)和888(每個顔色成分占8位)。注意alpha值有時候是不需要的。

        Direct3D将這些顔色模式定義為枚舉類型的值,你可以在表2.2中看到這一點。現在,比如說你想設定顯示模式是640×480并且使用D3DFMT_R5G6B5顔色格式。以下是你設定D3DDISPLAYMODE結構體的模式:

D3DDISPLAYMODE d3ddm;
d3ddm.Width            = 640;
d3ddm.Height         = 480;
d3ddm.RefreshRate    = 0;   // use default
d3ddm.Format         = D3DFMT_R5G6B5;
           
DirectX 9.0c遊戲開發手記之RPG程式設計自學日志之6: Drawing with DirectX Graphics (用DirectX圖形繪圖)(第2節)

        為了檢查顯示擴充卡是否可以操控你需要的顔色格式,用需要的資訊填充D3DDISPLAYFORMAT結構體,然後調用以下函數:

// g_pD3D = pre-initialized Direct3D object
// d3df = pre-initialized D3DFORMAT structure
// Check if display mode exists
if(FAILED(g_pD3D->CheckDeviceType(D3DADAPTER_DEFAULT,\
D3DDEVTYPE_HAL, &d3df, &d3df, FALSE))) {
    //Error occurred - color mode not supported
}
           

        設定顯示模式假定你在使用全屏模式。如果相反,你想支援視窗模式(例如标準的Windows應用程式),那就讓Direct3D為你填充顯示模式資訊。你用以下的函數調用來完成這一點:

// g_pD3D = pre-initialized Direct3D object
D3DDISPLAYMODE d3ddm;
 
if(FAILED(g_pD3D->GetDisplayMode(D3DADAPTER_DEFAULT,&d3ddm))) {
    //Error occurred
}
           

        注意:

==============================================================================

        正如所有的COM接口一樣,Direct3D傳回一個HRESULT值。傳回值為D3D_OK意味着函數調用是成功的;其他任何傳回值意味着調用失敗。你可以簡單地使用标準的FAILED或者SUCCEEDED宏來測試傳回的代碼。

==============================================================================

        如果成功的話,前面對IDirect3D9::GetDisplayMode的調用會傳回一個有效的D3DDISPLAYMODE結構體。

        當心:

==============================================================================

        某些顯示擴充卡不能夠使用特定的顯示模式。确定一個擴充卡能否支援各種模式是你的工作。如果你在使用視窗模式,這不是一個大問題,因為Direct3D會為你處理顔色模式設定。

==============================================================================

 2.2.5 Setting thePresentation Method (設定顯示方法)

        建立Direct3D的下一步是确定如何向使用者顯示圖形。你是想在視窗中、整個螢幕上還是在背景緩沖區(backbuffer)(更多有關背景緩沖區的内容,參見下一個注意事項)中顯示圖形呢?你想使用什麼樣的重新整理頻率呢?所有這些資訊(還有更多,正如你将會看到的)被儲存在一個D3DPRESENT_PARAMETERS結構體中:

typedef struct_D3DPRESENT_PARAMETERS
{
    UINT   BackBufferWidth; // Width of backbuffer
    UINT   BackBufferHeight; // Height ofbackbuffer
    D3DFORMAT BackBufferFormat; // Same as display mode format
    UINT BackBufferCount; // 1
 
    D3DMULTISAMPLE_TYPE MultiSampleType; // 0
    D3DSWAPEFFECT SwapEffect; // how to display backbuffer
 
    HWND hDeviceWindow; // NULL
    BOOL Windowed; // TRUE for windowed mode
                 // FALSE for fullscreen mode
    BOOL EnableAutoDepthStencil; // FALSE
    D3DFORMAT AutoDepthStencilFormat; // 0
 
    DWORD Flags; // 0
 
    UINT FullScreen_RefreshRateInHz; // 0
    UINT PresentationInterval;      // 0
}D3DPRESENT_PARAMETERS;
           

        注意

===============================================================================

        背景緩沖區是一個離屏的(off-screen)繪圖表面(與視窗或者視訊螢幕一樣大),它接受所有的繪制操作。為了看到繪制在背景緩沖區上的圖形,你用一種叫做翻轉(flip)的操作,它把背景緩沖區的内容先是到視訊螢幕或者視窗上。這個操作顯示了平滑的更新——除非你準備好了去顯示,否則使用者永遠不會看到正在被繪制的東西。

        你可以在圖2.9中看到這一概念,圖中顯示了前(顯示器)螢幕和後(off-screen)螢幕。你在後螢幕上繪制,然後當你繪制完畢時,你将兩個螢幕進行翻轉,使得後螢幕的内容顯示出來。

DirectX 9.0c遊戲開發手記之RPG程式設計自學日志之6: Drawing with DirectX Graphics (用DirectX圖形繪圖)(第2節)

===============================================================================

        雖然這個操作也許看上去很複雜,但是實際上你不需要管D3DPRESENT_PARAMETERS結構體中的大多數成員;然而,你确實需要了解與背景緩沖區有關的成員。

        這裡有你能夠使用的兩個可能的設定,使用哪個取決于你想要用視窗模式還是全屏模式(還有一小段示範如何使用兩種模式的代碼):

// d3ddm = pre-initialized
D3DDISPLAYMODE structure
D3DPRESENT_PARAMETERS d3dpp;
 
// Clear out the structure
ZeroMemory(&d3dpp,
sizeof(D3DPRESENT_PARAMETERS));
 
// For windowed mode, use:
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = d3ddm.Format; // use same color mode
 
// For fullscreen mode, use:
d3dpp.Windowed = FALSE;
d3dpp.SwapEffect = D3DSWAPEFFECT_FLIP;
d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
d3dpp.BackBufferFormat = d3ddm.Format; // use same color mode
 
// For both windowed and fullscreen, specify the width and height
d3dpp.BackBufferWidth = Width; // Supply your own width
d3dpp.BackBufferHeight = Height; // Supply your own height
 
           

2.2.6 Creating theDevice Interface and Initializing the Display (建立裝置接口并初始化顯示)

        終于你可以創造Direct3D裝置接口了,它正是3-D系統的重體力勞動者。使用你之前建立的D3DDISPLAYMODE和D3DPRESENT_PARAMETERS結構體,你通過調用IDirect3D9::CreateDevice函數來建立并初始化顯示接口:

HRESULT IDirect3D9::CreateDevice(
UINT       Adapter,// D3DADAPTER_DEFAULT
D3DDEVTYPE DeviceType, // D3DDEVTYPE_HAL
HWND       hFocusWindow,// window handle to use for rendering
DWORDBehavior Flags, // D3DCREATE_SOFTWARE_VERTEXPROCESSING
D3DPRESENT_PARAMETERS *pPresentationParameters, // d3dpp
IDirect3DDevice9 *ppReturnedDeviceInterface); // device object
           

        在CreateDevice函數中,你看到了在哪裡傳遞你建立的presentation結構體,還有屬于你的應用程式的視窗(并且Direct3D會使用它來顯示渲染後的圖形)的句柄。剩下的參數非常标準,你很少需要更改它們。最後的參數是你正在建立的Direct3D裝置對象的指針。IDirect3D9::CreateDevice函數的一個調用也許看起來像這樣:

// g_pD3D = pre-initialized Direct3D object
// hWnd = window handle to use for rendering
// d3dpp = pre-initialized presentation structure
IDirect3DDevice9* g_pD3DDevice;
if(FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT,\
        D3DDEVTYPE_HAL, hWnd, \
        D3DCREATE_SOFTWARE_VERTEXPROCESSING, \
        &d3dpp, &g_pD3DDevice))) {
  //Error occurred
}
           

         注意:

=============================================================================

        Direct3D與兩種不同的裝置協作——HAL(Hardware Abstraction Layer,硬體抽象層)和REF(REFerence device,引用裝置)。HAL裝置是你實際的圖形卡,并且這是你在開發遊戲時幾乎總要使用的裝置。REF裝置(一個軟體渲染對象)在你想調試你的應用程式時或者測試一種你目前的圖形卡不支援的特性時使用。HAL是二者中較快的,因為它允許Direct3D與你的圖形卡的硬體直接工作,而REF則在軟體中工作(并且在這方面非常慢)。

=============================================================================

2.2.7 Losing theDevice (失去裝置)

        一般來說,裝置接口會按照預期的那樣工作,一切都很棒;圖形被畫出來了,并且記憶體資源被維護着。雖然認為你的裝置會一直處于這種操作狀态的想法很棒,但是還是有一些時候是做不到的。那好,我們就進入失去裝置的世界吧。

        一個失去的裝置(lost device)是指因為這樣或者那樣的原因失去了對圖形資源的控制的裝置。可能是因為另外一個應用程式控制了你的圖形擴充卡并且耗光了儲存你的應用程式的圖形資料的記憶體。也可能是Windows在進入睡眠模式的時候降低了系統的動力消耗(powered down the system)。不管是什麼原因,你對圖形裝置的控制失去了,然後你需要把它奪回來。

        你如何直到什麼時候失去了控制呢?通過檢查你調用的任何裝置函數的傳回值!例如,在本章後面的一節“展示場景”中,你會看到如何将圖形先是到視訊顯示器上。在那個函數調用中,如果裝置對象傳回一個D3DERR_DEVICELOST的值,你會知道裝置失去了。

        重新獲得裝置的控制是很drastic的一步,從某種角度上說。它通過下面的函數而搞定:

HRESULT IDirect3DDevice9::Reset(
D3DPRESENT_PARAMETERS *pPresentationParameters);
           

        上述函數的唯一一個參數是拟用于初始化裝置用的presentation結構體:

//g_pD3DDevice = pre-initialized device object
// d3dpp = pre-setup presentation structure
g_pD3DDevice->Reset(&d3dpp);
           

        我本想說這是一個魔法般的函數,因為它在重建裝置的時候為你搞定了所有事情,但是我很抱歉要告訴你一些壞消息。調用這個reset函數會重置裝置并且清理掉所有的資源——這其實并不是太糟糕,因為有可能它們本來就已經丢失了(因為裝置丢失了呀)。

        最關鍵的就是你需要重新載入與圖形有關的所有的資源(例如紋理),并且你需要重建裝置狀态(the settings)。失去的很多東西是你還沒有學到的資料,是以我在将來會讓你跟上進度的。

2.2.8 IntroducingD3DX (介紹D3DX)

        處理Direct3D有時候是一件大任務。雖然微軟已經簡化了許多的接口,但是你還是需要做一些工作的。為了加快項目開發的速度,微軟創造了D3DX庫。D3DX庫充滿了處理圖形(例如網格、紋理、字型、數學等等)的有用的函數。在這本書中,你會看到如何利用D3DX庫來讓你的遊戲程式設計冒險變得更加順利一些。

        注意:

===============================================================================

        所有的D3DX函數都以D3DX字首開始(例如,D3DXCreateFont)。D3DX庫不僅僅包含函數,還包含COM對象,例如ID3DXBaseMesh。

===============================================================================

        注意:

===============================================================================

        為了在你的項目中使用D3DX庫,你需要包含進D3DX9.H以及連結到D3DX9.LIB(同樣,二者實際上是小寫的)。并且,你還可能要連結D3DXOF.LIB庫檔案,它稍後就會有用的。

===============================================================================

===============================================================================

        好啦,這個漫長的一節終于講述完畢了!如果你能夠消化的話,那麼恭喜你,你已經學會了DirectX程式設計中最困難的一部分!剩下的都是小case啦!

繼續閱讀