在以前的教程中,咱們創建了一個最小的Direct3D 11的應用程序,它用來在窗口上輸出一個單一顏色。在本次教程中,咱們將擴展這個應用程序,在屏幕上渲染出一個單一顏色的三角形。咱們將經過設置數據機構的過程關聯到三角形。git
這個教程的輸出結果是在窗口中央渲染出一個三角形。github
(SDK root)\Samples\C++\Direct3D11\Tutorials\Tutorial02數組
Github-LearnDirectX-DX3D11 tutorial02 (源碼已上傳至Github)ide
三角形由其三個點定義,也稱爲頂點。 具備惟一位置的一組三個頂點定義了惟一的三角形。 爲了讓GPU渲染三角形,咱們必須告訴它三角形的三個頂點的位置。舉一個2D的例子,假設咱們但願渲染一個三角形,例如圖1中的三角形。咱們將三個頂點與位置(0,0)(0,1)和(1,0)一塊兒傳遞給GPU,而後 GPU有足夠的信息來渲染咱們想要的三角形。佈局
圖1.由三個頂點定義的2D三角形spa
因此如今咱們知道咱們必須將三個位置傳遞給GPU才能渲染三角形。 咱們如何將這些信息傳遞給GPU? 在Direct3D 11中,諸如位置的頂點信息存儲在緩衝區資源中。 用於存儲頂點信息的緩衝區被稱爲頂點緩衝區,這並不奇怪。 咱們必須爲三個頂點建立一個足夠大的頂點緩衝區,並用頂點位置填充它。 在Direct3D 11中,應用程序必須在建立緩衝區資源時指定緩衝區大小(以字節爲單位)。 咱們知道緩衝區必須足夠大才能容納三個頂點,但每一個頂點須要多少字節? 要回答這個問題,須要瞭解頂點佈局。3d
頂點有一個位置。 一般,它還具備其餘屬性,例如法線,一種或多種顏色,紋理座標(用於紋理映射)等。 頂點佈局定義了這些屬性在內存中的位置:每一個屬性使用的數據類型,每一個屬性的大小以及內存中屬性的順序。 由於屬性一般具備不一樣的類型,相似於C結構中的字段,因此頂點一般由結構表示。 頂點的大小能夠方便地從結構的大小中得到。orm
在本教程中,咱們只處理頂點的位置。 所以,咱們使用XMFLOAT3類型的單個字段定義頂點結構。 此類型是三個浮點組件的向量,一般是用於3D位置的數據類型。對象
struct SimpleVertex { XMFLOAT3 Pos; // Position };
咱們如今有一個表示咱們的頂點的結構。 它負責在咱們的應用程序中將頂點信息存儲在系統內存中。 然而,當咱們向GPU提供包含頂點的頂點緩衝區時,咱們只是給它一塊內存。 GPU還必須知道頂點佈局,以便從緩衝區中提取正確的屬性。 要實現此目的,須要使用輸入佈局。blog
在Direct3D 11中,輸入佈局是Direct3D對象,它以GPU能夠理解的方式描述頂點的結構。 可使用D3D11_INPUT_ELEMENT_DESC結構描述每一個頂點屬性。 應用程序定義一個或多個D3D11_INPUT_ELEMENT_DESC的數組,而後使用該數組建立輸入佈局對象,該對象將頂點描述爲一個總體。 如今咱們將詳細介紹D3D11_INPUT_ELEMENT_DESC的字段。
SemanticName |
SemanticName是一個字符串,其中包含描述此元素的性質或目的(或語義)的單詞。 這個詞能夠是C標識符能夠的任何形式,也能夠是咱們選擇的任何形式。 例如,頂點位置的良好語義名稱是POSITION。 語義名稱不區分大小寫。 |
SemanticIndex |
SemanticIndex補充了語義名稱。 頂點能夠具備相同性質的多個屬性。 例如,它能夠具備2組紋理座標或2組顏色。 不是使用附加了數字的語義名稱,例如「COLOR0」和「COLOR1」,這兩個元素能夠共享單個語義名稱「COLOR」,具備不一樣的語義索引0和1。 |
Format |
格式定義要用於此元素的數據類型。 例如,DXGI_FORMAT_R32G32B32_FLOAT的格式有三個32位浮點數,使元素長12個字節。 DXGI_FORMAT_R16G16B16A16_UINT的格式有四個16位無符號整數,使元素長8個字節。 |
InputSlot |
如前所述,Direct3D 11應用程序經過使用頂點緩衝區將頂點數據傳遞給GPU。 在Direct3D 11中,能夠同時向GPU提供多個頂點緩衝區,準確地說是16。 每一個頂點緩衝區都綁定到0到15之間的輸入槽號.InputSlot字段告訴GPU它應該爲該元素獲取哪一個頂點緩衝區。 |
AlignedByteOffset |
頂點存儲在頂點緩衝區中,頂層緩衝區只是一塊內存。 AlignedByteOffset字段告訴GPU開始獲取此元素數據的內存位置。 |
InputSlotClass |
該字段的值一般爲D3D11_INPUT_PER_VERTEX_DATA。 當應用程序使用實例化時,它能夠將輸入佈局的InputSlotClass設置爲D3D11_INPUT_PER_INSTANCE_DATA以使用包含實例數據的頂點緩衝區。 Instancing是一個高級的Direct3D主題,這裏再也不討論。 對於咱們的教程,咱們將專門使用D3D11_INPUT_PER_VERTEX_DATA。 |
InstanceDataStepRate |
該字段用於實例化。 因爲咱們不使用實例化,所以不使用此字段,必須將其設置爲0。 |
如今咱們能夠定義D3D11_INPUT_ELEMENT_DESC數組並建立輸入佈局:
// Define the input layout D3D11_INPUT_ELEMENT_DESC layout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; UINT numElements = ARRAYSIZE(layout);
在下一個教程中,咱們將解釋技術對象和關聯的着色器。 目前,咱們將專一於爲該技術建立Direct3D 11頂點佈局對象。 可是,咱們將瞭解頂點着色器與此頂點佈局緊密耦合。 緣由是建立頂點佈局對象須要頂點着色器的輸入簽名。 咱們使用從D3DX11CompileFromFile返回的ID3DBlob對象來檢索表示頂點着色器的輸入簽名的二進制數據。 得到此數據後,咱們能夠調用ID3D11Device :: CreateInputLayout()來建立頂點佈局對象,並使用ID3D11DeviceContext :: IASetInputLayout()將其設置爲活動頂點佈局。 完成全部這些操做的代碼以下所示:
// Create the input layout if( FAILED( g_pd3dDevice->CreateInputLayout( layout, numElements, pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), &g_pVertexLayout ) ) ) return FALSE; // Set the input layout g_pImmediateContext->IASetInputLayout( g_pVertexLayout );
在初始化期間咱們還須要作的一件事是建立保存頂點數據的頂點緩衝區。 要在Direct3D 11中建立頂點緩衝區,咱們填寫兩個結構D3D11_BUFFER_DESC和D3D11_SUBRESOURCE_DATA,而後調用ID3D11Device :: CreateBuffer()。
D3D11_BUFFER_DESC描述了要建立的頂點緩衝區對象,D3D11_SUBRESOURCE_DATA描述了在建立過程當中將複製到頂點緩衝區的實際數據。 頂點緩衝區的建立和初始化是一次完成的,所以咱們之後不須要初始化緩衝區。 將複製到頂點緩衝區的數據是頂點,即三個簡單結構的數組。 選擇頂點數組中的座標,以便在使用着色器渲染時在應用程序窗口的中間看到一個三角形。 建立頂點緩衝區後,咱們能夠調用ID3D11DeviceContext :: IASetVertexBuffers()將其綁定到設備。 完整的代碼以下所示:
// Create vertex buffer 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 );
原始拓撲是指GPU如何得到渲染三角形所需的三個頂點。 咱們在上面討論過,爲了渲染單個三角形,應用程序須要向GPU發送三個頂點。 所以,頂點緩衝區中有三個頂點。 若是咱們想渲染兩個三角形怎麼辦? 一種方法是將6個頂點發送到GPU。 前三個頂點定義第一個三角形,後三個頂點定義第二個三角形。 此拓撲稱爲三角形列表。 三角形列表具備易於理解的優勢,但在某些狀況下它們效率很是低。 當連續渲染的三角形共享頂點時會出現這種狀況。 例如,圖3a左側顯示了由兩個三角形組成的正方形:ABC和CB D.(按照慣例,三角形一般經過按順時針順序列出它們的頂點來定義。)若是咱們使用三角形列表將這兩個三角形發送到GPU ,咱們的頂點緩衝區會這樣:
A B C C B D
請注意,B和C在頂點緩衝區中出現兩次,由於它們由兩個三角形共享。
圖3a包含一個由兩個三角形組成的正方形; 圖3b包含由三個三角形組成的五邊形。
若是咱們能夠告訴GPU在渲染第二個三角形時,咱們可使頂點緩衝區更小,而不是從頂點緩衝區獲取全部三個頂點,使用前一個三角形中的2個頂點,並從頂點緩衝區中僅獲取1個頂點。 事實證實,這是由Direct3D支持的,拓撲結構稱爲三角形條帶。 渲染三角形條帶時,第一個三角形由頂點緩衝區中的前三個頂點定義。 下一個三角形由前一個三角形的最後兩個頂點加上頂點緩衝區中的下一個頂點定義。 以圖3a中的方塊爲例,使用三角形條帶,頂點緩衝區看起來像:
A B C D
前三個頂點A B C定義第一個三角形。 第二個三角形由B和C定義,即第一個三角形的最後兩個頂點加上D.所以,經過使用三角形條帶拓撲,頂點緩衝區大小從6個頂點變爲4個頂點。 相似地,對於三個三角形,例如圖3b中的三角形,使用三角形列表將須要頂點緩衝區,例如:
A B C C B D C D E
使用三角形條帶,頂點緩衝區的大小顯着減小:
A B C D E
在咱們的代碼中,咱們有一個三角形,因此咱們指定的並不重要。 可是,咱們必須指定一些內容,所以咱們選擇了三角形列表。
// Set primitive topology g_pImmediateContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
缺乏的最後一項是執行三角形實際渲染的代碼。咱們建立了兩個用於渲染的着色器,頂點着色器和像素着色器。頂點着色器負責將三角形的各個頂點轉換爲正確的位置。像素着色器負責計算三角形的每一個像素的最終輸出顏色。這將在下一個教程中詳細介紹。要使用這些着色器,咱們必須分別調用ID3D11DeviceContext :: VSSetShader()和ID3D11DeviceContext :: PSSetShader()。咱們作的最後一件事是調用ID3D11DeviceContext :: Draw(),它命令GPU使用當前頂點緩衝區,頂點佈局和原始拓撲進行渲染。 Draw()的第一個參數是要發送到GPU的頂點數,第二個參數是要開始發送的第一個頂點的索引。由於咱們渲染一個三角形而且咱們從頂點緩衝區的開頭渲染,因此咱們分別使用3和0做爲兩個參數。整個三角形渲染代碼以下所示:
// Render a triangle g_pImmediateContext->VSSetShader( g_pVertexShader, NULL, 0 ); g_pImmediateContext->PSSetShader( g_pPixelShader, NULL, 0 ); g_pImmediateContext->Draw( 3, 0 );