DirectX 9.0c遊戲開發手記之RPG編程自學日誌之8: Drawing with DirectX Graphics (用DirectX圖形繪圖)(第4節)(A)

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

 

        這一次我們繼續來講述Jim Adams老哥的RPG編程書籍第二版第二章的第4節:Getting Down to Drawing。這一節可以說是超級長了,所以我們就分3次來講吧!

 

        由於這一節的內容實在是太多,所以我這一節的各小節的標題列在下面,以供大家參考:


1、Using Vertices (使用頂點)

2、Flexible Vertex Format (靈活頂點格式)

3、Using Vertex Buffers (使用頂點緩存)

4、Vertex Streams (頂點流)

5、Vertex Shaders (頂點着色器)

6、Transformations(變換)

7、The World Transformation (世界變換)

8、The View Transformation (視角變換)

9、The Projection Transformation (投影變換)

10、Materials and Colors (材質和顏色)

11、Clearing the Viewport (清除視口)

12、Beginning and Ending a Scene (開始和結束場景)

13、Rendering Polygons (渲染多邊形)

14、Presenting the Scene (展示場景)

 

        這一期要從Using Vertices講到Vertex Shaders。

 

        原文翻譯:

 

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

 

2.4 Getting Down to Drawing (開始進行繪製)

 

        基礎部分講的足夠多了;是時候來看看Direct3D到底是怎麼繪製圖形的。在這一節中,我會介紹使用頂點和多邊形來繪製圖形的基本知識。你會學到Direct3D使用頂點來繪製多邊形的各種方式,如何給這些多邊形上色,並最終向用戶展示這些圖形。正所謂細節決定成敗,因此要好好研究處理頂點的方法,再從這裏繼續前進。

 

 

2.4.1 Using Vertices (使用頂點)

 

        Direct3D給了你很多不同的方式來定義一個頂點。例如,如果你在使用2-D圖形,你可以在2-D屏幕座標(變換後的座標)中設定座標。

        另一方面,如果你在使用局部的或者世界的空間座標,你可以在3-D座標(未變換的座標)中設定座標。那麼如何使用顏色和紋理呢?你可以在你的頂點定義中選擇包含進這些信息。

        那麼你如何跟蹤所有的這些信息並且保證Direct3D直到你正在做什麼呢?請看靈活頂點格式。

 

 

2.4.2 Flexible Vertex Format (靈活頂點格式)

 

        靈活頂點格式(flexible vertex format,簡稱FVF)用於構造自定義的頂點數據以便在你的應用程序中使用。使用FVF,你必須決定你的頂點要使用哪些信息——例如3-D座標、2-D座標、顏色等等。

        你使用一種標準的結構體來創建FVF,在這個結構體中你只加入你想要的成分。當然有一些限制,例如你必須用某種特定的順序來列舉這些成分,並且某些成分不能與其他的成分相沖突(例如同時使用2-D座標和3-D座標)。一旦這個結構體完成後,你建立一個FVF描述器(FVF descriptor),它是描述你的頂點格式的一系列標記的組合。

        下面的代碼塊包含了一個使用了各種在FVF中允許使用的變量(或者說,至少是所有的我在這本書中用到的變量)的頂點結構體。結構體中的變量列舉的順序與它們在你自己的結構體中出現的順序嚴格保持一致;如果你截去了任何變量,保證剩下的保持順序:

 

typedef struct {
  FLOAT            x,y, z, rhw;       // 2-D coordinates
  FLOAT            x,y, z;            // 3-D coordinates
  FLOAT            nx,ny, nz;         // Normals
  D3DCOLOR <span style="white-space:pre">	</span>   diffuse;           // Diffuse color
  FLOAT            u,v;               // Texturecoordinates
} sVertex;


        正如你所看到的,唯一的互相沖突的變量是表示座標的變量,包括法向量(normals)。法向量是用來定義一個方向的並且只能與3-D座標聯合使用的座標。你需要選擇哪些座標(要麼2-D,要麼3-D)要保留,哪些座標要丟棄。如果你在使用2-D座標,那麼你就不能包含進3-D座標;反之亦然。

        2-D座標和3-D座標的唯一的真正的區別是額外的rhw變量,它是其次座標(homogeneous)W的倒數。用通俗的話來說(原文是In English,這一般代表從觀察點到頂點的沿着z-軸的距離。在大多數情況下,你可以安全地將rhw值設爲1.0。

        還要注意,sVertex座標使用了數據類型FLOAT(它是一個浮點值),但是D3DCOLOR是啥數據類型呢?D3DCOLOR是一個DWORD值,在Direct3D中,你用之來儲存顏色值。爲了構造一個顏色值用於D3DCOLOR,你可以從兩個函數中進行選擇:D3DCOLOR_RGBA或者D3DCOLOR_COLORVALUE:

D3DCOLORD3DCOLOR_RGBA(Red, Green, Blue, Alpha);
D3DCOLOR D3DCOLOR_COLORVALUE(Red,Green, Blue, Alpha);

        每個函數(事實上它們是宏)取四個參數,代表着要用到的各個顏色成分的量,包括一個alpha值(透明度)。這些值在D3DCOLOR_RGBA中可以從0變到255,而在D3DCOLOR_COLORVALUE中可以從0.0變到1.0(分數)。如果你在使用實心的顏色(不透明的顏色),那麼就把alpha值恆取爲255(或1.0)。

        作爲例子,比如說你只想在你的頂點結構中包含進3-D座標和一個漫反射顏色成分:

typedef struct {
  FLOAT            x,y, z;
  D3DCOLOR diffuse;
} sVertex;

        創建你的FVF的下一步是使用表格2.3中列出來的任意標記組合來建立FVF描述器。

 

表2.3 靈活頂點格式描述器標記

Flag

Description

D3DFVF_XYZ

包含進了3-D座標。

D3DFVF_XYZRHW

包含進了2-D座標。

D3DFVF_NORMAL

包含了法向量(一個向量)。

D3DFV_DIFFUSE

包含進了一個漫反射顏色成分。

D3DFVF_TEX1

包含進了一組紋理座標。

 

        爲了描述一個FVF描述器,你把所有適當的標記組合進一個define語句中(假設你在使用3-D座標和漫反射顏色成分):

#define VertexFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE)

        只需要保證所有的標記與你加入到你的頂點結構體中的成分相匹配,那麼一切就會順利的。


2.4.3 Using Vertex Buffers (使用頂點緩存)

 

        在你構造你的頂點結構體以及描述器之後,你要創建一個包含了一個頂點數組的對象。Direct3D給了你兩個對象來使用:IDirect3DVertexBuffer9和IDirect3DIndexBuffer9。我在這本書中使用的對象是IDiret3DVertexBuffer9,它儲存着用於繪製三角形列、三角形帶和三角形扇的頂點。(實際上,將它與IDirect3DIndexBuffer9結合起來使用纔是比較省效率的方法;這也正是本人在自己的更新版代碼中使用的方法。)

 

        當用於三角形列時,IDirect3DVertexBuffer9對象爲每個要繪製的多邊形儲存至少3個頂點(這些頂點以順時針順序排序)。對於三角形帶,第一個將被繪製的多邊形使用3個頂點,而後面的每個要被繪製的多邊形只需要使用1個額外的頂點。對於三角形扇,儲存有一箇中心的頂點,而每一個將被繪製的多邊形要儲存兩個額外的頂點(其實一般來說只要一個額外的頂點,見下面的正方形例子)

 

        注意:

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

        一個多邊形可以使用1個、2個或3個頂點,這取決於你在繪製什麼圖形。像素只需要一個單個的頂點,線段需要兩個,而三角形多邊形需要三個。在這本書中,我主要處理的是三角形的多邊形。

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

 

        圖2.11應該可以幫助你更好地理解你如何使用儲存的頂點以及你以什麼順序佈置這些頂點。在圖中,有一個可以用三種方法之一定義的正方形。在第一種方法中,是使用一個三角形列,所以你需要使用6個頂點來定義這個正方形——兩個三角形的每一個都要花去三個頂點。


        第二種方法是使用一個三角形帶。三角形帶只使用4個頂點,這在圖中表示得很明白。前三個頂點構造了第一個面,而最後一個多邊形定義了第二個面。對於第三種方法,也就是三角形扇方法,你還是使用4個頂點。但是,用三角形扇時,你定義的第一個頂點變成了扇的基點,而其餘的頂點定義了扇面。


 

 

2.4.3.1 Creating a Vertex Buffer (創建一個頂點緩存)


        你通過初始化後的IDirect3DDevice9對象來創建一個頂點緩存:

HRESULT IDirect3DDevice9::CreateVertexBuffer(
  UINT              Length,    // # of bytes to use, in multiples
                               //of vertex structure size.
  DWORD      Usage,    <span style="white-space:pre">	</span>       // 0
  DWORD        FVF,            // FVF descriptor
  D3DPOOL   Pool,              // D3DPOOL_MANAGED
  IDirect3DVertexBuffer9 **ppVertexBuffer,         // the vertex buffer
  HANDLE       *pHandle);      //set to NULL

        在CreateVertexBuffer函數的調用中你唯一想要改變的參數是Usage標記,它告訴Direct3D如何對待用於儲存頂點數據的內存。爲了呆在安全的一邊,你應該始終將Usage設爲0,但是如果你想提升一點性能的話,將Usage設爲D3DCREATE_WRITEONLY。這樣做會告訴Direct3D你不準備讀取頂點數據,然後Direct3D會適當地儲存頂點數據。一般來說,這意味着頂點數據會儲存在圖形硬件的內存中(稱爲:快速讀取內存,faster-access memory)。


        這是一個簡短的例子(建立在我之前——在“靈活頂點格式”這一節中——的頂點格式基礎上,它只使用了3-D座標和漫反射頂點成分),它創建了一個包含4個頂點的頂點緩存:

// g_PD3DDevice = pre– initialized device object
// sVertex =pre-defined vertex structure
// VertexFVF =pre-defined Vertex FVF descriptor
IDirect3DVertexBuffer9*PD3DVB = NULL;
 
// Create the vertexbuffer
if(FAILED(g_PD3DDevice->CreateVertexBuffer(                  \
    sizeof(sVertex) * 4, D3DCREATE_WRITEONLY, VertexFVF,     \
    D3DPOOL_MANAGED, &PD3DVB, NULL))) {
    // Error occurred
}


 

        注意:

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

        在創建你自己的頂點緩存時,確保你設定了合適的緩存大小(CreateVertexBuffer函數調用的第一個參數)。在這裏展示的例子中,你開闢了足夠的內存來儲存4個sVertex格式的頂點。這裏的4表示4個實例,而sVertex是你用於儲存你的頂點數據的結構體。

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

 

        注意:

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

        跟以前一樣,在你用完頂點緩存後,確保通過調用其Release函數來釋放這個COM對象。

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

 

2.4.3.2 Locking the Vertex Buffer (鎖定頂點緩存)

 

        在你能夠將頂點添加進頂點緩存對象之前,你必須鎖定該緩存所使用的內存。這保證存儲頂點的內存是位於可利用的內存區域。然後你使用一個內存指針來訪問頂點緩存內存。你通過調用緩存對象的Lock函數來鎖定頂點緩存的內存並返回一個內存指針:

HRESULTIDirect3DVertexBuffer9::Lock(
  UINT    OffsetToLock,  // offset to lock buffer, in bytes
  UINT    SizeToLock,    // how many bytes to lock, 0 = all
  VOID **ppbData,        // pointer to a pointer (to access data)
  DWORD Flags);          //0

        在這裏,你讓offset位於緩存中你想要訪問的位置處(以字節數來計算),並且指定你想要訪問的字節數目(0代表全部)。然後你需要做的全部就是向函數傳遞指向你將用來訪問頂點緩存的內存指針的指針(轉型成爲一個VOID數據類型)。下面是一個鎖定整個頂點緩存的調用的示例:

// pD3DVB =pre-intialized vertex buffer object
BYTE *Ptr;
// Lock the vertexbuffer memory and get a pointer to it
if(FAILED(pD3DVB->Lock(0,0, (void**)&Ptr, 0))) {
    // Error
}
 


        注意:

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

        在其創建時使用D3DCREATE_WRITEONLY標記的頂點緩存無法進行讀取,只能夠寫入。如果你想從一個頂點緩存讀取數據(就像D3DX中某些函數那樣),你應該在你調用CreateVertexBuffer函數時將Usage參數設爲0。

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

 

        在你結束了對頂點緩存的訪問之後,始終要記得在每個Lock函數的調用之後跟上一個IDirect3DVertexBuffer9::Unlock函數的調用:

HRESULTIDirect3DVertexBuffer9::Unlock();

        解鎖一個頂點緩存確保Direct3D可以開始安全地使用它了,因爲它知道你不會再修改該緩存內的任何數據了。

 

        當心!

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

        確保最小化在調用Lock函數和調用Unlock函數之間的時間量。你處理被鎖定的頂點緩存越快則越好,因爲Direct3D必須停下來等你弄完頂點緩存,以便能夠訪問其內部包含的數據。

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

 

2.4.3.3 Stuffing in Vertex Data (填充頂點數據)


        現在你有了你的頂點結構體,描述器以及緩存,並且你已經鎖定了緩存並準備儲存頂點數據。因爲你已經從Lock函數的調用中得到了指向頂點緩存內存的數據指針,你所需要做的就是將適當數量的頂點複製進頂點緩存。

        繼續我的例子,並利用我已經定義的頂點格式(使用3-D格式和漫反射顏色成分),我在一個數組內部創建了一個局部的頂點數據集合:

sVertex Verts[4] = {
  {  -100.0f, 100.0f, 100.0f, D3DCOLOR_RGBA(255, 255,255, 255)  },
  {   100.0f, 100.0f, 100.0f, D3DCOLOR_RGBA(255, 0, 0,255)  },
  {   100.0f, -100.0f, 100.0f, D3DCOLOR_RGBA(0, 255, 0,255)  },
  {  -100.0f,  -100.0f, 100.0f, D3DCOLOR_RGBA(0, 0, 255, 255) }
};


        鎖定頂點緩存,這樣得到一個指向頂點緩存內存的指針,然後複製這個局部的頂點數據(並且在弄完之後解鎖頂點緩存):

 

// pD3DVB =pre-initialized vertex buffer object
BYTE *Ptr;
 
// Lock the vertexbuffer memory and get a pointer to it
if(SUCCEEDED(pD3DVB->Lock(0,0, (void**)&Ptr, 0))) {
 
  // Copy local vertices into vertex buffer
  memcpy(Ptr, Verts, sizeof(Verts));
 
  // Unlock the vertex buffer
  pD3DVB->Unlock();
}


        這就是構建一個頂點緩存並往裏填充頂點數據要做的所有事情!要使用頂點信息,現在你只需要指定一個流源(stream source)和頂點渲染器(vertex shader)。

 

 

2.4.4 Vertex Streams (頂點流)

 

        Direct3D讓你能夠通過一系列不同的流(stream)——稱爲頂點流(vertex streams)——將頂點餵給(feed to)渲染器(renderer)。你可以通過將多個流的頂點數據整合到一個流中來創造非常令人印象深刻的結果,但是在這本書中,我只是用一個流,因爲使用多個流的複雜性已經超出本書範圍了。

        爲了將你的頂點數據複製到一個流中,你使用IDirect3DDevice9::SetStreamSource函數:

 

HRESULTIDirect3DDevice9::SetStreamSource(
  UINT    StreamNumber,       // 0
  IDirect3DVertexBuffer9* pStreamData,     // Vertex buffer object
  UINT    OffsetInBytes,          // Offset (in bytes) of vertex data
                                                     //in the vertex buffer.
  UINT    Stride);                        // Size of vertexstructure


        你現在設定頂點流源(vertex stream source)要做的所有事情就是調用這個函數,並傳遞指向頂點緩存對象的指針、提供用於存儲頂點結構體的字節數(使用sizeof)。

        如果你在頂點緩存中存儲着多餘一組多邊形(比如幾個三角形帶),你可以將OffsetInBytes參數設爲頂點數據的offset。例如,如果我使用頂點緩存來儲存兩個三角形帶(第一個三角形帶位於offset 0處,而第二個三角形帶位於offset 6處),那麼我通過設置適當的offset(在這種情況下,第二個三角形帶的offset爲6)來繪製第二個三角形帶。

        從上一節中在頂點緩存中儲存頂點的例子出發,你可以使用下面的方法:

// g_pD3DDevice –pre-initialized device object
// pD3DVB =pre-initialized vertex buffer
if(FAILED(g_pD3DDevice->SetStreamSource(0,pD3DVB,                         \
0, sizeof(sVertex)))) {
    // Error occurred
}

 

2.4.5 Vertex Shaders (頂點着色器)

 

        作爲使用頂點來繪製圖形的最後一步,你需要理解頂點着色器的概念。頂點着色器(vertex shader)是這樣一種機制,它操控頂點的載入和處理;這具體包括改變頂點座標、運用顏色和霧化功能以及很多其他的頂點組件。

        一個頂點着色器可以以兩種形式出現。它可以是固定的(fixed)頂點着色器(其中所有的用於完成一般功能的函數都已經是內建的了),也可以是可編程的(programmable)頂點着色器(其中你可以在渲染到顯示屏之前自定義函數來改變頂點信息)。

        嘗試去解釋可編程頂點着色器超出了本書的範圍。(然而,本人在更新版的代碼中一直使用的是用HLSL語言寫的可編程頂點着色器。)相反,我集中於使用固定的頂點着色器,因爲它們包含了你所需要的所有的功能。(未必;在十年前你可能認爲足夠了,但是對於新一代的玩過很多酷炫遊戲的我們來說,這是遠遠不夠的。)

        爲了在你的頂點上使用固定頂點着色器,你將你的自定義頂點的FVF描述器傳遞給IDirect3DDevice9::SetFVF函數中:

HRESULTIDirect3DDevice9::SetFVF(
  DWORD        FVF);         // Vertex FVF descriptor


        使用前面的函數跟下面一樣簡單:

// g_pD3DDevice =pre-initialized device object
// VertexFVF =pre-defined vertex FVF descriptor
if(FAILED(g_pD3DDevice->SetFVF(VertexFVF))){
    // Error occurred
}


        你現在已經建立了頂點信息了。下一個步驟是建立將頂點(在局部空間中)放置到其世界空間座標時所需要的各種各樣的變換。當然,只有你在使用3-D座標的時候才需要這一步。


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

 

        好了,這幾個小節的內容總算講完了。可能讀者看得不是太明白,尤其是那些函數的具體參數的意義之類的。其實這個大家可以參考SDK文檔,或者參考龍書第二版。另外這裏所用的設置頂點的方法在本人的更新版代碼中是不會使用的,而是用的是龍書第二版中更加先進、更加靈活的方式,所以看得不太懂也沒什麼關係。


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