程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++Directx11開發筆記三:繪制圖形

C++Directx11開發筆記三:繪制圖形

編輯:C++入門知識

在前面我們講過了如何初始化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;
 屬性說明:


  SemanticName:用來描述目的或名稱的字符,只要任何符合C語言結構的字符串都可以使用,並且忽略大小寫,比如用於描述頂點坐標可以使用“POSITION”字符串。
SemanticIndex:這個用來描述索引,當SemanticName相同的情況下就可以使用索引來描述到底哪個才是當前需要的。因為一個頂點可能包含多個顏色,或紋理坐標等等,例如多個顏色可以使用如COLOR0,COLOR1來描述,也可以都是用COLOR來描述,使用0和1來填充SemanticIndex。
Format:描述這個數據的數據類型,如:DXGI_FORMAT_R32G32B32_FLOAT描述3個32位的float數據類型,即12字節長度;而DXGI_FORMAT_R16G16B16A16_UINT表示4個16位的uint數據類型,即8字節長度。
InputSlot:輸入槽,在前面提到過的,每一個頂點信息都通過一個Vertex Buffer輸入讓GPU識別,在Direct 3D 11中多個頂點信息可以同時的輸入,最多可以有16個,這樣就需要讓GPU知道當前將使用哪個Vertex Buffer信息,也就是這個值將在0到15之間了。
AlignedByteOffset:內存偏移量,告訴GPU當前緩存內存起始偏移量。
InputSlotClass:這個一般使用D3D11_INPUT_PER_VERTEX_DATA來填充,當使用實例數據類型時,將使用D3D11_INPUT_PER_INSTANCE_DATA,這個是比較高級的或許在我們以後的學習當中會遇到,這裡我們不大清楚,就先放過。
InstanceDataStepRate:這個用於D3D11_INPUT_PER_INSTANCE_DATA時候,如果不是則必須將其設置為0。
 了解了上面的信息,我們就可以定義我們自己的

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 );
}
 最終顯示結果如下:

 

 \

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved