在前面我們講過了如何初始化D3D11Device裝置初始化等等,這裡所講的繪製圖形將在上一篇文章的項目裡進行擴充,在螢幕中繪製圖形。在3D的呈現中最小的單位為三角形,無論我們看到的是多麼大或多麼小的,都是有一個或很多個三角形通過各種方向,角度構成的,當然這會涉及到很多數學中的幾何學問題,最悲劇的就是我在大學裡卻沒學好代數以及幾何學,有學也忘記了。不過Directx SDK中以及為我們解決了很多幾何上的問題,通過他們的方法就可以得到結果,說了這麼多目的就是我告訴大家,要掌握高階運用,必然要學會基礎知識,所以我們這裡就來學習一下如何在螢幕上繪製一個三角形,並塗上顏色。
一個三角形由三個點組成,也可以說座標。在座標系裡,三個不同的點就可以組成一個唯一的三角形,當然也就是唯一的面,我想3D圖形是由很多個面組成的,這也就是最小單位為三角形的原因。為了能夠讓GPU(就是顯卡中的CPU,簡單的認為一下,(*^__^*) )呈現三角形,我們必須告訴他三個點的座標,那樣他才能夠在螢幕中顯示出來。例如:在2D中,我們要在螢幕中畫出如下的圖示的三角形,就必須告訴GPU他們三個頂點(0,0),(0,1),(1,0)的座標,那樣GPU才能夠畫出他們。
我們已經知道要把頂點告訴GPU,但是如何告訴他們呢?在Direct3D 11中,頂點資訊如三角形三個頂點的座標是儲存在一個緩衝資源中的,叫做頂點緩衝(Vertex Buffer)。我們必須建立一個足夠大的頂點緩衝,讓他能夠承載三角形的三個頂點座標資訊。
INPUT LAYOUT
一個頂點不只包含一個座標,還可能包含一個或多個顏色值,紋理座標等等,而Input Layout就是定義這些資訊如何在記憶體中儲存:不同資料類型將會有不同的大小, 當然不同的大小也決定著不同的儲存順序。和C語言很像,一個頂點一般使用一個結構來定義。在這裡,我們只需要定義一個三角形頂點的座標,所以我們只要使用XMFLOAT3來定義一個座標,具體代碼如下:
// 3D Vector; 32 bit floating point components
typedef struct _XMFLOAT3
{
FLOAT x;
FLOAT y;
FLOAT z;
#ifdef __cplusplus
_XMFLOAT3() {};
_XMFLOAT3(FLOAT _x, FLOAT _y, FLOAT _z) : x(_x), y(_y), z(_z) {};
_XMFLOAT3(CONST FLOAT *pArray);
_XMFLOAT3& operator= (CONST _XMFLOAT3& Float3);
#endif // __cplusplus
} XMFLOAT3;
// 以上是XMFLOAT3的結構資訊,在3D中座標的結構
struct SimpleVertex
{
XMFLOAT3 Pos; // Position
};
我們定義了一個SimpleVertex結構,就是為了儲存三角形的頂點座標,為了能夠讓GPU瞭解並且能夠在記憶體中獲得相關資訊,我們就必須使用到Input Layout。在Direct 3D 11中,一個Input Layout被定義成能夠讓GPU識別的結構資訊,每一個頂點屬性可以使用一個叫D3D11_INPUT_ELEMENT_DESC結構來進行描述。不同的頂點資訊可以通過定義一個數組來解決這個問題,那樣每一個頂點都能夠進行描述,下面讓我們來瞭解一下這個結構的具體屬性。
typedef struct D3D11_INPUT_ELEMENT_DESC {
LPCSTR SemanticName;
UINT SemanticIndex;
DXGI_FORMAT Format;
UINT InputSlot;
UINT AlignedByteOffset;
D3D11_INPUT_CLASSIFICATION InputSlotClass;
UINT InstanceDataStepRate;
} D3D11_INPUT_ELEMENT_DESC;
屬性說明:
瞭解了上面的資訊,我們就可以定義我們自己的
D3D11_INPUT_ELEMENT_DESC了,具體代碼如下所示:
1 // Define the input layout
2 D3D11_INPUT_ELEMENT_DESC layout[] =
3 {
4 { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
5 };
6 UINT numElements = ARRAYSIZE( layout );
Vertex Layout
頂點布局主要就是為了給頂點著色器(Vertex Shader)提供計算的【註:也許這個描述不正確】,為了建立一個頂點布局就需要頂點著色器輸入簽名,我們使用ID3DBlob介面對象來描述,而ID3DBlob介面通過D3DX11CompileFromFile來檢索頂點著色器包含簽名的位元據。只要我們有了這個資料,就可以通過ID3D11Device::CreateInputLayout()方法來建立我們的Vertex Layout對象,和使用ID3D11DeviceContext::IASetInputLayout()方法來啟用它,具體代碼如下:
2 if( FAILED( g_pd3dDevice->CreateInputLayout( layout, numElements, pVSBlob->GetBufferPointer(),
3 pVSBlob->GetBufferSize(), &g_pVertexLayout ) ) )
4 return FALSE;
5 // Set the input layout
6 g_pImmediateContext->IASetInputLayout( g_pVertexLayout );
建立Vertex Buffer
知道了上面的內容我們還需要做一件事,在初始化時我們必須建立一個Vertex Buffer並且承載了這個頂點的資料資訊。為了建立Vertex Buffer,必須填充兩個結構D3D11_BUFFER_DESC和D3D11_SUBRESOURCE_DATA,然後使用ID3D11Device::CreateBuffer()方法進行建立。D3D11_BUFFER_DESC結構對Vertex Buffer要建立的內容對象進行描述,而
D3D11_SUBRESOURCE_DATA則是包含了具體的資料資訊,這些資料資訊在建立時會進行拷貝。建立緩衝和初始化是在同一時間完成的,在建立後我們可以使用ID3D11DeviceContext::IASetVertexBuffers()方法將其綁定到裝置中,那樣我們就可以在螢幕上呈現出來了,具體代碼如下:SimpleVertex vertices[] =
{
XMFLOAT3( 0.0f, 0.5f, 0.5f ),
XMFLOAT3( 0.5f, -0.5f, 0.5f ),
XMFLOAT3( -0.5f, -0.5f, 0.5f ),
};
D3D11_BUFFER_DESC bd;
ZeroMemory( &bd, sizeof(bd) );
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof( SimpleVertex ) * 3;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;
bd.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA InitData;
ZeroMemory( &InitData, sizeof(InitData) );
InitData.pSysMem = vertices;
if( FAILED( g_pd3dDevice->CreateBuffer( &bd, &InitData, &g_pVertexBuffer ) ) )
return FALSE;
// Set vertex buffer
UINT stride = sizeof( SimpleVertex );
UINT offset = 0;
g_pImmediateContext->IASetVertexBuffers( 0, 1, &g_pVertexBuffer, &stride, &offset );
Primitive Topology (原型拓撲結構)
從上面我們可以得知,如果要呈現一個三角形那樣就需要將三個頂點資訊告知GPU,那樣如果兩個三角形就必須告訴GPU6個頂點的資訊,如果一個四邊形(兩個三角形組成),也就是說有兩個頂點是共有的,PrimitiveTopology就是為瞭解決這個問題的。在四邊形中,只要傳入是個頂點資訊,就可以畫出四邊形了,,就可以很好的理解了。
如,如果要呈現3a中的圖,只要告訴GPU是個頂點,GPU就會直接畫出這個四邊形了,當然也要注意一下順序:A B C D,其實在Vertex Buffer中描述的是A B C和B C D,這樣 B C兩點是共用的,當然在3b圖形中也一樣。我們只要設定如下代碼就可以得到,如下所示:
g_pImmediateContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
接下來就是畫出三角形,以上這些都是在裝置初始化後進行的。而畫出三角形就需要用到頂點和像素著色器,具體代碼如下:
void Render()
{
// Clear the back buffer
float ClearColor[4] = { 0.0f, 0.125f, 0.3f, 1.0f }; // red,green,blue,alpha
g_pImmediateContext->ClearRenderTargetView( g_pRenderTargetView, ClearColor );
// Render a triangle
g_pImmediateContext->VSSetShader( g_pVertexShader, NULL, 0 );
g_pImmediateContext->PSSetShader( g_pPixelShader, NULL, 0 );
g_pImmediateContext->Draw( 3, 0 );
// Present the information rendered to the back buffer to the front buffer (the screen)
g_pSwapChain->Present( 0, 0 );
}
最終顯示結果如下:
寫到了這裡差點瀏覽器掛掉,OK今天就到這裡,發現這個部落格園的代碼編輯器在Google瀏覽器裡還不是很好用,第一行代碼老是要跑到外面出去。