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();