1. 程式人生 > >Director3D中的繪製——頂點快取和索引快取

Director3D中的繪製——頂點快取和索引快取

一個頂點快取是儲存有頂點資料的連續空間,一個索引快取是儲存有索引資料的連續空間,使用頂點快取和索引快取來表示相關的資料,而不使用陣列來表示的原因是因為:頂點快取和索引快取儲存在視訊記憶體中,在處理的時候能夠獲得比系統記憶體更快的處理速度。

在程式碼中,頂點快取使用的介面IDirectorVertexBuffer9表示,索引快取使用的是IDirectorIndexBuffer9表示。

建立頂點快取和索引快取

建立頂點緩:

HRESULT IDirect3DDevice9::CreateVertexBuffer(
	UINT Length,
	DWORD Usage,
	DWORD FVF,
	D3DPOOL Pool,
	IDirect3DVertexBuffer9** ppVertexBuffer,
	HANDLE* pSharedHandle
);

建立索引快取:

HRESULT IDirect3DDevice9::CreateIndexBuffer(
	UINT Length;
	DWORD Usage,
	D3DFORMAT Format,
	D3DPOOL Pool,
	IDirect3DIndexBuffer9** ppIndexBuffer,
	HANDLE* pShareHandle
);

有一些引數是相同的,放在一起解釋:

Length:為快取分配的位元組數,如果你想讓快取能夠儲存5個頂點,那麼就需要給Length賦值:5 * sizeof(Vertex),其中Vertex是我們定義的頂點的結構體

Usage:指定使用頂點的一些附加屬性,可以是0,也可以是下面的屬性中的一個或者多個的結合

              D3DUSAGE_DYNAMIC:將快取設定為動態快取,預設的是靜態快取

              D3DUSAGE_POINTS:該標記規定快取用來儲存點圖元,僅僅適用於頂點快取

              D3DUSAGE_SOFTWAREPROCESSING:指定頂點軟體運算方式

              D3DUSAGE_WRITEONLY:規定應用程式對軟體的操作模式是“只寫”的,這樣驅動程式就可以將快取放在最適合寫操作的記憶體地址中去,注意:對使用該操作的快取進行“讀”操作的時候會出錯

Pool:記憶體池的型別

pShareHandle:微軟文件中給的意思,貌似這個引數是用於Windows Vista,用來共享資源的,我們不適用時設定成NULL,就很穩健

CreateVertexBuffer9中的引數解釋:

FVF:頂點的靈活頂點格式

ppVertexBuffer:執行接收頂點快取地址的指標(也就是傳說中的二級指標)

CreateIndexBuffer9中的引數解釋:

Format:指定索引的大小,有兩哥固定的選擇:D3DFMT_INDEX16和D3DFMT_INDEX32,分別表示16位索引和32位索引,但是不是所有的圖形卡都支援32位的索引,使用時需檢測。

ppIndexBuffer:指向索引快取地址的指標(還是傳說中的二級指標)

Tip:當我們未在引數Usage中使用D3DUASGE_DYNAMIC的時候,建立的快取屬於靜態快取,當我們使用這個關鍵字的時候,建立的快取屬於動態快取,我們使用靜態快取的時候通常是資料屬於不經常修改的,此時我們使用靜態快取,比如:地形或者城市的建築,這些就是不經常修改的資料,我們就可以將它建立為靜態資料。但是一些經常被修改的資料,比如粒子系統中的粒子就是不斷地被修改,此時就應該使用動態快取。

訪問快取內容

為了訪問頂點快取或者索引快取,我們需要先去獲取指向快取內部儲存區的指標,獲取指向快取內部儲存區的指標的時候,我們可以藉助Lock方法,很重要的一點當我們在訪問完成之後,我們一定要使用Unlock對訪問的區域進行解鎖,只要獲取了指向快取內部的儲存區的指標,我們就可以訪問快取了。

訪問頂點快取:

HRESULT IDirect3DVertexBuffer9::Lock(
	UINT OffsetToLock,
	UINT SizeToLock,
	BYTE** ppbData,
	DWORD Flags
);

訪問索引快取:

HRESULT IDirect3DIndexBuffer9::Lock(
	UINT OffsetToLock,
	UINT SizeToLock,
	BYTE** ppbData,
	DWORD Flags
)

引數解析:

OffsetToLock:自快取區域起始位置的偏移量,單位是:位元組

SizeToLock:需要訪問的位元組數

ppbData:指向要訪問快取區域的起始位置

Flags:該標記標誌了一些鎖定方式,可以是0,也可以是下面選項的組合

             D3DLOCK_DISCARD該標記僅用於動態快取。它指示硬體將快取內容丟棄,並返回一個指向重新分配的快取的指標。該標記1十分有用,因為這允許在我們訪問新分配的記憶體時,硬體能夠繼續使用被丟棄的快取中的資料進行繪製,這樣硬體的繪製就不會中止。
             D3DLOCK_NOOVERWRITE 該標記僅用於動態快取。使用該標記後,資料只能以追加方式寫入快取。即您不能覆蓋當前用於繪製的儲存區中的任何內容。這十分有用,因為它可保證在您往快取中增加資料時,硬體仍可持續進行繪製。
             D3DLOCK READONLY 該標記表示對於您所鎖定的快取只可讀而不可寫。利用這一點可以作一些內部的優化。

注意:其中標記D3DLOCK_DISCARD和D3DLOCK_NOOVERWRITE,這兩個標誌代表快取區中的內容被鎖定之後還是可以用於繪製的。如果硬體裝置允許這些操作的時候,在對快取進行鎖定的時候,其他的顯示操作就不會被中斷。

下面是使用Lock的例子:
 

Vertex* vertices;
_vb->Lock(0,0,(void**)&vertices,0);

vertices[0] = Vertex(-1.0f, 0.0f, 2.0f);
vertices[1] = Vertex(-1.0f, 0.0f, 2.0f);
vertices[2] = Vertex(-1.0f, 0.0f, 2.0f);

_vb->Unlock();

獲取頂點快取和索引快取的資訊

如果我們有時候需要獲取頂點快取和索引快取的資訊,可以使用以下方法:

//獲取vertex資訊
D3DVERTEXBUFFER_DESC vbDescription;
vertexBuffer->GetDesc(&vbDescripiton);

//獲取index資訊
D3DINDEXBUFFER_DESC ibDescription;
indexbuffer->GetDesc(&ibDescription);

上面的兩個結構體的定義是:

typedef struct _D3DVERTEXBUFFER_DESC 
{
	D3DFORMAT Format;
	D3DRESUORCETYPE Type;
	DWORD Usage;
	D3DPOOL Pool;
	UINT size,
	DWORD FVF
}D3DVERTEXBUFFER_DESC;

typedef struct  
{
	D3DFORMAT Format,
	D3DRESOURCETYPE Type,
	DWORD Usage,
	D3DPOOL Pool,
	UINT size
}D3DINDEXBUFFER_DESC;

繪製狀態

Director3D中封裝了很多繪製狀態,繪製狀態將影響幾何體的繪製方式,各種繪製狀態均具有預設值,如果你不修改,就一直繪製預設的繪製狀態,知直到你修改了他。

修改繪製狀態的方法如下:

HRESULT IDirect3DDevice9::SetRenderState(
	D3DRENDERSTATETYPE State,		//準備改變成的狀態
	DWORD Value					    //新狀態的值
)

如果我們使用線框的繪製模式來繪製幾何體的話,就是下面這麼用:

_decice->SetRenderState(D3DRS_FILLMODE, D3DFILE_WIREFRAME);

繪製的準備工作

一旦我們建立好了頂點快取以及索引快取(可以不使用,也可以使用),我們就應該準備開始繪製了。

繪製之前還需要3個動作需要完成:

1、將頂點快取和資料流進行繫結,實質就是將頂點資訊傳入到資料流中,也就是傳入到繪製流水線(方法見下方)

2、設定頂點格式,之後在呼叫的時候,會使用頂點格式,需要現在設定好。下面是例子:

_device->SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1);

3、設定索引快取,如果我們建立了索引快取,並且在之後需要使用到索引快取,我們就必須對索引快取進行設定,因為在繪製的過程中同一時間只能使用一個索引快取,所以在繪製不同的索引快取的時候,我們就需要進行切換索引快取。方法如下:

_device->SetIndices(_ib)        //_ib是指向索引快取的指標

將頂點快取和資料流進行連結繫結的方法:

HRESULT IDirect3DDevice9::SetStreamSource(
	UINT StreamNumber,
	IDirect3DVertexBuffer9* pStreamData,
	UINT OffsetInBytes,
	UINT Stride
);

引數解析:
StreamNumber:標識與頂點快取建立連結的資料流,不使用多資料流的話就將資料設定為:0

pStreamData:指向我們希望與資料流建立連結的指向頂點快取的指標

OffsetInBytes:自資料流的起始點開始算的偏移量,指定了被傳輸到資料流中的頂點快取在資料流中的起始位置

Stride:將要連結到資料流中的頂點快取中頂點的元素大小,也就是頂點的結構體的大小

使用頂點快取和索引快取對幾何體進行繪製

我們使用的繪製方式介面是:DrawPrimitive()或者DrawOIndexPrimitive(),這兩個介面從資料流中讀取頂點資訊,來進行繪製。

函式介紹:DrawPrimitive()

使用情況:繪製未使用索引快取的圖元

IDirect3DDevice9::DrawPrimitive(
	D3DPRIMITIVETYPE PrimitiveType,
	UINT StartVertex,
	UINT PromitiveCount
);

PrimitiveType:所要繪製的圖元型別。我們除了繪製三角形之外,還可以繪製點和線,如果要繪製三角形就是:D3DPT_TRIANGLELIST,繪製點就是:D3DPT_POINTLIST,繪製線就是:D3DPT_LINELIST,這些都是D3DPRIMITIVETYPE的列舉型別,可以很輕鬆的在文件中查詢到。

StartVertex:頂點資料流中標誌標識資料讀取起始點的元素的索引,該引數可以使我們只去繪製幾何體的一部分。

PromitiveCount:所要繪製的元素的圖元數量。

函式介紹:DrawIndexPrimitive()

使用方式:繪製使用了索引快取的圖元

IDirect3DDevice9::DrawIndexedPrimitive(
	D3DPRIMITIVETYPE PrimitiveType,
	INT  BaseVertexIndex,
	UINT MinIndex,
	UINT NumVertices,
	UINT StartIndex,
	UINT PrimitiveCount
)

PrimitiveType:所要繪製的圖元型別。我們除了繪製三角形之外,還可以繪製點和線,如果要繪製三角形就是:D3DPT_TRIANGLELIST,繪製點就是:D3DPT_POINTLIST,繪製線就是:D3DPT_LINELIST,這些都是D3DPRIMITIVETYPE的列舉型別,可以很輕鬆的在文件中查詢到。

BaseVertexIndex:為索引增加一個基數,也就是在全域性頂點快取中的一個偏移量

MinIndex:允許引用的最小索引值

Numvertices:本次呼叫中將引用的頂點的總數

StartIndex:頂點快取中標識索引的讀取起始點的元素的索引

PrimitiveCount:所要繪製的圖元總數

說明:

關於引數BaseVertexIndex,假如我們現在需要將球,圓柱體,箱子繪製出來,區域性索引快取和區域性頂點快取是對應的,但是如果我們現在要將球,圓柱體和箱子的頂點放在一起,放在全域性頂點快取中去,那麼我們在分別繪製對應的幾何體的時候,就需要用自己的索引在全域性的頂點快取中找到他對應的頂點部分,怎麼找,我們可以通過一個偏移量來找,而現在DrawIndexPrimitive允許我們通過BaseVertexIndex引數來傳入這個偏移量,可以參見下圖:

各頂點的索引快取分別獨立於各事物

最後一點

所有的繪製必須在BeginScene 和 EndScene之間:

例如:

_device->BeginScene();
			  _device->DrawPrimitive(...);
_device->EndScene();