Modeling(建模)

   前一章講解的主要是怎樣向direct3D 描述場景。場景裏面的物體都是由很多組primitives組成的,primitive包括point, line, triangles。 由於三角形也是一個面,所以一個三角形就可以模擬出一個平滑的表面。一個頂點除了位置信息,面法線外,還有很多額外的信息綁定到這個頂點,如頂點顏色值,紋理座標值,頂點blend weight,任意的shader數據等等。 這些數據是通過一組數據流傳遞給Device,每一個流都對應一個頂點buffer。

   Direct3D 提供了兩種方法來描述一個物體的形狀,可以用參數把面的精細度定義成真實面的一組三角形。設備一次只描述場景中的一個primitive 。

 

建模場景

   所有的3D 渲染都在在場景裏面完成的,BeginScene標誌着一個場景的開始,緊接着是圖形渲染,EndScene標誌着場景渲染的結束。設備則是被創建以後就可以設置它的屬性和狀態,他們不受場景的影響。StretchRect 不是一個渲染方法,能被應用在場景之外。

 

可視性

   "Z buffer"是一個判斷可視性的算法,它工作在Render Target的每個像素上,而不是每個模型上。 當一個模型被光柵化之後,每一個像素就會有一個Z 值,來表示這個像素離照相機的距離。這個算法也不是完美的,雖然對於不透明的像素還能處理的很好,但是如處理那些果透明的或者半透明的像素,它就有缺陷了。幸運的是,我們可以先畫出所有不透明的物體,然後把透明的物體從後到前排序,然後用painter算法畫它們。 這個方法並不完美,它確實能減少artifacts,但是仍然使用了快速Z buffer硬件。

   depth/stencil buffer 存放着像素離照相機的距離。 一個depth/stencil buffer是通過設備的D3DPRESENT_PARAMETERS來創建的,或者顯示的調用CreateDepthStencilSurface。GetDepthStencilSurface可以取得設備的depth/stencil surface。爲了利用Z buffer來解決可視性問題,設置RS Z enable 爲 D3DZB_TRUE, RS Z Write Enable爲True 和 RS Z Func 爲D3DCMP_LESS。

   如果 D3DCAPS9::RasterCaps 的 D3DPRASTERCAPS_ZBUFFERLESSHSR被設置,它意味着用另外一種方法做可視性判斷,可視性判斷是與硬件相關,這種情況下render targe的depth/stencil surface 爲空,而且設置RS Z enable爲 D3DZB_TRUE.

 

Render Targets

   當光柵處理結束後,像素流就產生了。它的目的地就是設備的render target。 Swap chain上的所有的back buffer 都是合理的render targets,但是render target並不僅僅限制於back buffer的surface。當設備被創建或者reset的時候,render target就是默認swap chain的back buffer 0。當調用present的時候,render target就跳到下一個back buffer,這樣當present 返回以後,render target又回到back buffer 0。

   可以把render target設置一個表面,這樣就允許設備直接渲染到一張圖片上。非swap chain 上的render target surface 可以通過調用函數CreateRenderTarget。

   SetRenderTarget ,GetRenderTarget, Clear 方法用於對render target surface進行操作。

 

Primitive Type

   一般包括D3DPT_POINTLIST, D3DPT_LINELIST, D3DPT_LINESTRIP,D3DPT_TRIANGLELIST,D3DPT_TRIANGLESTRIP, D3DPT_TRIANGLEFAN。

頂點數據

   場景數據由定義物體形狀和外表的數據組成。我們已經看到形狀可以由primitive和頂點座標確定。用於固定流水線的數據要麼是FVF格式的數據,要麼是頂點shader聲明格式的數據。頂點shader程序可以在每個頂點上使用任意的數據。每個與頂點相關的數據都被稱作是頂點的component。利用FVF格式的數據,所有的數據必須在一個單一的流裏面。但是頂點shader聲明可以把這些components分成多個流。

   除了選擇固定功能處理和可編程頂點處理外,應用程序還可以把數據直接傳遞給設備,而不進行任何的頂點處理。

 

靈活頂點格式(FVF)

   頂點FVF是一個DWORD,它包含一個或者多個描述在內存裏面頂點組件,如:D3DFVF_XYZ, D3DFVF_XYZRHW, D3DFVF_XYZB1,等等。

   位置相關的FVF包括D3DFVF_XYZ, D3DFVF_XYZRHW,D3DFVF_XYZB1, D3DFVF_XYZB3,D3DFVF_XYZB4 和 D3DFVF_XYZB5。B值是它的blending weight。

   D3DFVF_PSIZE 與點精靈有關,描述了點精靈的大小。

   D3DFVF_TEXn標記紋理座標的數目,D3DFVF_TEXCOORDSIZEN 紋理座標的維度。

   GetFVF和SetFVF用於FV格式的管理F。

 

頂點聲明

   使用頂點聲明,可以將頂點數據分成多個流,每個流都包含一個或多個頂點組件數據。頂點聲明可以被用於固定功能處理也可以用於可編程處理。

   頂點聲明是由一組D3DVERTEXELEMENT9結構的數組創建的。數組裏面的每個元素代表頂點裏面一個component。元素在數組裏面的順序會影響流裏面數據的分佈。但是,頂點組件順序可以存放在內存裏面的任意位置。

typedef struct _D3DVERTEXELEMENT9

{

   WORD Stream; //

   WORD Offset;

   BYTE   Type; //數據的類型,如果D3DECLTYPE_FLOAT3,理論上所有的數據在被傳入到頂點處理之前都要是4值的向量,如果不是,則會在y,z補0,w補1。

   BYTE Method; // 用於頂點的tessellation。

    BYTE Usage; // 如: D3DECLUSAGE_POSITION。

    BYTE UsageIndex; //用於有同一個Usage的索引,如哪個texture stage。

}

   頂點聲明的接口是IDirect3DVertexDeclaration9,它有兩個只讀的方法,GetDeclaration和GetDevice。

   當固定功能流水線使用頂點聲明時,每個頂點組件必須被映射到一個特定的usage。你能使用多個stream,不過這個流必須遵循FVF順序和頂點類型。

   注意:頂點聲明總是存放在系統內存,所以當設備重啓之後它不需要恢復。

 

Vertex Buffer

   Vertex buffer 資源用於存儲應用程序的頂點數據。Direct3D 的接口是IDirect3DVertexBuffer9。

interface IDirect3DVertexBuffer9 : IUnknown

{

HRESULT GetDesc(D3DVERTEXBUFFER_DESC* value);

HRESULT Lock(UINT offset, UINT size, BYTE** data , DWORD flags);

HRESULT Unlock();

}

   創建一個包含size 個字節的vertex buffer,可以通函數CreateVertexBuffer();

   HRESULT CreateVertexBuffer(UINT size, DWORD usage, D3DPOOL pool, DWORD fvf,IDirect3DVertexBuffer9 ** reuslt, Handle * unused);

   如果fvf參數爲0 ,它將創建一個non-FVF vertex buffer,其內容將使用頂點shader聲明。 unused必須是NULL。 usage 包括D3DUSAGE_DONOTCLIP, D3DUSAGE_DYNAMIC, D3DUSAGE_NPATCHES,D3DUSAGE_POINTS, D3DUSAGE_RTPATCHES, D3DUSAGE_SOFTWAREPROCESSING, D3DUSAGE_WRITEONLY.

   D3DUSAGE_DONOTCLIP指這些頂點不需要clipping; D3DUSAGE_NPATCHES, D3DUSAGE_POINTS, D3DUSAGE_RTPATCHES 指這個頂點buffer將分別被用於畫 N-Patches, 點 sprite,higher order suface patches;D3DUSAGE_SOFTWAREPROCESSING 指頂點用於軟件處理;D3DUSAGE_WRITEONLY指這個buffer不能讀取,只能寫入;D3DUSAGE_DYNAMIC指應用程序可能會改變裏面的內容,如果不指定這個標記,頂點buffer將是靜態的。

    如果D3DCAPS9::DevCaps的D3DDEVCAPS_TLVVERTEXSYSTEMMEMORY 或者 D3DDEVCAPS_TLVVERTEXVIDEOMEMORY 被設置,設備能夠使用video 或者system內存中的而且包含被轉換頂點的vertex buffer。通常static vertex buffer是分配在設備內存中的,然而動態頂點buffer則是在系統內存中或者AGP內存,可以被CPU直接訪問。動態頂點buffer必須要在系統內存池中分配,或者在默認內存池中分配。

    IDirect3DVertexBuffer9::GetDesc將會返回一個D3DVERTEXBUFFER_DESC的結構。

typedef struct _D3DVERTEXBUFFER_DESC

{

   D3DFORMAT Format; //對於vertex buffer,將永遠是D3DFMT_VERTEXDATA;

   D3DRESOURCETYPE Type;

   DWORD Usage;

   D3DPOOL Pool;

   UINT   Size;

   DWORD FVF;

}

   IDirect3DVertexBuffer::Lock提供對頂點數據的直接訪問。對buffer 數據的訪問必須與它的參數flags相一致。 flags 包括D3DLOCK_DISCARD,D3DLOCK_NOOVERWRITE, D3DLOCK_NOSYSLOCK, D3DLOCK_READONLY, D3DLOCK_DISCARD指應用程序不關心以前的內容,以前的內容將會被拋棄;D3DLOCK_NOOVERWRITE 指應用程序不能重寫裏面的數據;D3DLOCK_DISCARD和D3DLOCK_NOOVERWRITE只能用於動態頂點buffer。對於動態幾何的場景,不正確lock將對render的性能影響很大。

 

被索引的Primitives

    我們已經知道在描述一個形狀的時候有很多重複的頂點,如果我們爲每個頂點分配一個序號,並且把所有不同的頂點按照序號緩存起來,這樣當我們製作一個primitive的時候,我們只需要提供primitive 頂點序號即可。這樣我們節省了很大存儲空間。

   Direct3D通過IDirect3DIndexBuffer9 接口來管理index buffer。這個接口的方法與IDirect3DVertexBuffer類似,這裏就不做詳細講解了。

 

頂點Shader

   設備的頂點shader屬性是在固定功能頂點處理和可編程頂點處理之中選擇。這些屬性就是頂點shader的接口指針,它是通過SetVertexShader和GetVertexShader來管理。設置這個接口指針爲空,表示選擇固定功能的處理,合理的接口指針,它將是意味着可編程的頂點處理。

HRESULT GetVertexShader(IDirect3DVertexShader9 ** value);

HRESULT GetVertexShader(IDirect3DVertexShader9 * value);

可以通過CreateVertexShader創建一個頂點shader,

HRESULT CreateVertexShader(const WORD * function, IDirect3DVertexShader9 ** result);

interface IDirect3DVertexShader9 : IUnknown

{

   HRESULT GetDevice(IDirect3DDevice9 ** value);

   HRESULT GetFunction(void * value, DWORD * size);
}

   通常一個應用程序將不會直接分配一個DWORD功能數組,但通常是通過D3DX在運行時編譯彙編語言或者更高級shader語言爲這個DWORD功能數組。

 

繪製Primitives

   一旦頂點數據被定義,並且頂點處理被配置,應用程序可以調用一個draw 方法進行渲染。draw方法很多,包括DrawPrimitiveUp,DrawIndexedPrimitiveUp,DrawPrimitive,DrawIndexedPrimitive,DrawTriPatch,DrawRectPatch等等。

    HRESULT DrawPrimitiveUp(D3DPRIMITIVETYPE kind, UINT primitive_count, const void * vertex_data, UINT vertex_stride); 這個方法繪製類型爲kind的非索引的頂點數據,它的內部處理是先將輸入的數據拷貝到一個新的vertex buffer, 然後繪製裏面的primitive,最後在釋放這個vertex buffer。創建,釋放以及拷貝數據將是非常昂貴的操作,這個方法只能被用於測試或者渲染primitive 比較少的場景。

   HRESULT DrawIndexedPrimitiveUp 與DrawPrimitiveUp類似,只是它是繪製被索引的頂點。

 

頂點數據流

   頂點各個組件可以來自與一條或者多條數據流,這些流數據組合起來就是一個完整的頂點。這些組合起來的頂點只是direct3D pipeline流水線的第一步。這些流數據都有一個編號,並且都有一個相關的vertex buffer和一個strid(頂點組件的大小)。流的編號是連續,以0開始,按照編號順序被連接。一個流必須包含一個或者數個頂點組件。

   不同的頂點組件到不同的流,可以讓應用程序動態的改變一個模型中頂點組件的值,而不用鎖住整個頂點buffer只改變其中某個部分的值。使用多個流,我們可以將頂點裏面的靜態組件放在一個流,而動態組件放在另外一個流。這允許我們可以在動態組件流的vertex buffer上使用D3DLOCK_DISCARD。可以通過HRESULT SetStreamSource(UINT index, IDirect3DVertexBuffer9 ** value, UINT offset, UINT stride)來設置與流關聯的vertex buffer。 stride參數是頂點組件的大小,對於一個FVF vertex buffer ,它應該與FVF一樣大,對於非FVF,它應該大於或者等於頂點聲明計算出來的大小;offset是距離流的開始端的字節偏移量。如果D3DCAPS9::Caps2裏面設置了D3DDEVCAPS2_STREAMOFFSET,才能支持偏移offset。

   流的最大數目是定義在D3DCAPS9::MaxStream。對於DirectX 8.1 或者以後的版本,這個值一般是1到16。但在DirectX 8.1 之前,這個值是0,暗示它只支持單一流。 MaxStreamStride給出了最大的流組件數據的大小。

   流sources提供頂點數據,並且也必須供應索引buffer的索引數據。SetIndices爲被索引的primitive指定索引數據。可能存在高達16個頂點數據流,但是隻有一個索引buffer。GetIndices返回索引屬性值。

   隨着所有的頂點組件和索引被載入,固定功能的頂點處理被選擇,stream source被正確的選擇,並且所有必須的狀態被設置,就可以分別調用DrawPrimitive和DrawIndexedPrimitive。

   HRESULT DrawIndexedPrimitive(D3DPRIMITIVETYPE kind, UINT base_vertex_index, UINT min_index, UINT number_vertices, UINT start_index, UINT primitive_count); min_index 指頂點buffer裏面的最小的頂點索引,start_index參數是在索引buffer裏面的偏移,base_vertex_index參數是將索引buffer的值都加上這個值,根據所得的結果再去從頂點buffer裏面取,這樣就允許多組流被打包成一組索引流,而不需要改變索引值。

   設備的Draw primitive方法,將primitive的批量處理轉化成一個單獨方法的調用。這是最有效的方法,將primitive傳送給設備。這個批量處理考慮了CPU 和設備之間更多的併發情況:當設備正在光柵化一組primitives的時候,cpu正準備傳遞下一組。

   D3DCAPS9的MaxPrimitiveCount給出了能夠被draw 方法使用的最大primitive數目。MaxVertexIndex給出了最大的頂點索引值。如果DevCaps的D3DDEVCAPS_DRAWPRIMTLVERTEX被設置,則設備有drawprimitive的硬件支持。

 

增強型頂點 

   Direct3D 提供了兩種對基本primitive的增強方法(點Sprites和N-Patches)。點Sprites 可以讓一個點primitves有一個用紋理化過的屏幕空間。N-Patches 可以提高了三角形的外觀,並且不需要做很多的其他工作。

   Point sprites 可以使D3DPT_POINTLIST primitives 被光柵化後不只是佔據一個像素的空間。如果D3DCaps9::MaxPointSize大於1,則設備支持點Sprites。Point sprites被渲染成屏幕空間裏的紋理化的方塊。點的大小可以在每個頂點的vertex buffer 用D3DFVF_POINT_SIZE來指定。

   如果point sprites使用是D3DFVF_XYZRHW,point sprites 的大小則是在屏幕空間的,否則這個size 必須要經過插值了的。

   point sprites 可以用於渲染一個symbol的許多實例,把symbol的圖片存儲在紋理裏面,然後把symbol的位置作爲point sprites。這種渲染方式,它的開銷只有直接使用頂點渲染的1/4。point sprites渲染方式也可以用於渲染粒子系統,每個粒子都是一個point sprites。

   N-patches

   N-patches 基於頂點和表面法線信息爲三角形做tessellation。如果D3DCAPS::DevCaps的D3DDEVCAPS_NPATCHES被設置,則設備支持N-patch。

   因爲三角形近似光滑平面,N-patches 提供爲surface 模型提供額外的detail,而不需要巨大的編程工作或者改變模型。當設備的N-patch mode屬性大於1.0的時,N-Patches可以用於三角形。基於原有的三角形頂點和法線信息重建三角形光滑表面的patch,然後再根據N-patch屬性細分那個patch,這樣就產生了額外的三角形。頂點位置和頂點法線都可以產生對應的patches。新頂點位置patch的代數degree定義在RS Position Degree,新頂點法線patch的degree定義在RS Normal Degree。這兩個render state值都是定義在D3DDEGREETYPE枚舉類型裏。RS position Degree可以是線性的或者立體型的,立體型是它的默認值。RS Normal degree也可以是線性的或者二次的,線性是它的默認值。

 

Higher order Surfaces(高維曲面)

   三角形和矩形patch提供了高質量參數化定義的primitives,這讓應用程序可以指定真正smooth的曲面,而不只是一種真實面的三角形模擬。這裏的面定義爲高維,因爲在數學裏面曲面的方程要比三角形方程次數要高。

   高維曲面又稱爲spline 面,在計算機圖形學裏面已經有很多算法和研究了。

   爲了定義一個三角形,我們一般定義它的每個頂點。如果想要定義面的patch,我們則需要提供控制點和一個patch產生函數,這個函數用來從控制點產生patch。曲面將會從控制點控制的數學方程集合中產生。這些數學方程集合通常稱爲patch的基本方程。

   每一個patch都通過基本函數來構造它的表面以及它的維度。D3DBASISTYPE和D3DORDERTYPE分別指定了它的基本方程和維度。Bezier基本方程的控制點是曲線的兩個端點以及曲線附件的點;B-spline方程的控制點不必在曲線上;插值方程保證所有的控制點在曲線上。

typedef enum _D3DBASISTYPE

{

D3DBASIS_BEZIER = 0,

D3DBASIS_BSPLINE = 1,

D3DBAISS_INTERPOLATE = 2
}

   Direct3D處理高維曲面的方法是把面鑲嵌到三角形集合,然後在後續的流水線裏面作進一步的處理。鑲嵌結果將會被緩存,並且將綁定到一個patch handle,在後續的patch渲染過程中就可以直接使用這個handle。

   如果D3DCAPS9:: DevCaps的D3DDEVCAPS_RTPATCHES被設置,這個設備就支持直接用DrawTriPatch和DrawRectPatch。如果D3DDEVCAPS_QUINTICRTPATCHES被設置,它將支持Bezier和B-spline RT patch。如果D3DDEVCAPS_PATCHHANDLEZERO被設置,它將意味着繪製未緩存和緩存的patch將會一樣的高效。

   三角行和矩形patch分別使用DrawTriPatch和DrawRectPatch來渲染。一個patch的鑲嵌會有大量的計算,所以緩存是很有意義的。如果handle爲空,則會根據patch_info進行鑲嵌。

HRESULT DeletePatch(UINT handle);

HRESULT   DrawRectPatch(UINT handle, const float * num_segments, const D3DRECTPATCH_INFO * patch_info);

HRESULT DrawTriPatch(UINT handle, const float *num_segments, const D3DTRIPATCH_INFO * patch_info);

   number_segments對於三角形是三個float的數組,對於矩形是四個float的數組,這允許更加精確的渲染一個patch的邊。

   鑲嵌結果可以直接通過硬件做渲染。或者按照傳統方法三角形化然後渲染。

  • 三角形patches

   在DrawTriPatch,patch_info 參數定義了一個三角形patch集合。Direct3D 對於三角形patch支持線性,立方型,二次型bezier模型。patch 的控制點的頂點數據也是存放在頂點buffer裏面。

Baiss   Degree count

Bezier    Linear   3

             CUBIC   10

             Quintic 21

typedef struct _D3DTRIPATCH_INFO

{

   UINT StartVertexOffset; // 當前流裏面第一個控制點的頂點索引。

   UINT NumVertices;

    D3DBASISTYPE Basis; //只能是D3DBASIS_BEZIER

     D3DDEGREETYPE Degree;

} D3DTRIPATCH_INFO;

 

  • 矩形patches

Baiss                Degree                 width                    height

Bezier               Linear                   2                            2

                         Cubic                    4                           4

                         Quintic                  6                           6

B-Spline            Linear                  1                              1

                          Cubic                    3                             3

                           Quintic                  5                            5

Catmull-Rom      Cubic                  3                               3

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