移植頂點緩衝區和資料
重要 API
- ID3DDevice::CreateBuffer
- ID3DDeviceContext::IASetVertexBuffers
- ID3D11DeviceContext::IASetIndexBuffer
在此步驟中,您將定義包含網格的頂點緩衝區,以及允許著色器以指定順序周遊頂點的索引緩衝區。
此時,讓我們檢查我們所使用的立方體網格硬式編碼模型。 這兩個表示法都有組織為三角形清單的頂點 (相對於帶狀結構或其他更有效率的三角形配置)。 這兩個表示法中的所有頂點也都有相關聯的索引和色彩值。 本主題中的大部分 Direct3D 程式碼都是指 Direct3D 專案中定義的變數和物件。
以下是 OpenGL ES 2.0 處理的立方體。 在範例實作中,每個頂點都是 7 個浮點值:3 個位置座標,後面接著 4 個 RGBA 色彩值。
#define CUBE_INDICES 36
#define CUBE_VERTICES 8
GLfloat cubeVertsAndColors[] =
{
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.0f, 1.0f
};
GLuint cubeIndices[] =
{
0, 1, 2, // -x
1, 3, 2,
4, 6, 5, // +x
6, 7, 5,
0, 5, 1, // -y
5, 6, 1,
2, 6, 3, // +y
6, 7, 3,
0, 4, 2, // +z
4, 6, 2,
1, 7, 3, // -z
5, 7, 1
};
以下是 Direct3D 11 處理的相同立方體。
VertexPositionColor cubeVerticesAndColors[] =
// struct format is position, color
{
{XMFLOAT3(-0.5f, -0.5f, -0.5f), XMFLOAT3(0.0f, 0.0f, 0.0f)},
{XMFLOAT3(-0.5f, -0.5f, 0.5f), XMFLOAT3(0.0f, 0.0f, 1.0f)},
{XMFLOAT3(-0.5f, 0.5f, -0.5f), XMFLOAT3(0.0f, 1.0f, 0.0f)},
{XMFLOAT3(-0.5f, 0.5f, 0.5f), XMFLOAT3(0.0f, 1.0f, 1.0f)},
{XMFLOAT3( 0.5f, -0.5f, -0.5f), XMFLOAT3(1.0f, 0.0f, 0.0f)},
{XMFLOAT3( 0.5f, -0.5f, 0.5f), XMFLOAT3(1.0f, 0.0f, 1.0f)},
{XMFLOAT3( 0.5f, 0.5f, -0.5f), XMFLOAT3(1.0f, 1.0f, 0.0f)},
{XMFLOAT3( 0.5f, 0.5f, 0.5f), XMFLOAT3(1.0f, 1.0f, 1.0f)},
};
unsigned short cubeIndices[] =
{
0, 2, 1, // -x
1, 2, 3,
4, 5, 6, // +x
5, 7, 6,
0, 1, 5, // -y
0, 5, 4,
2, 6, 7, // +y
2, 7, 3,
0, 4, 6, // -z
0, 6, 2,
1, 3, 7, // +z
1, 7, 5
};
檢閱此程式碼時,您會注意到 OpenGL ES 2.0 程式碼中的立方體會以右側座標系統表示,而 Direct3D 特定程式碼中的立方體則以左側座標系統表示。 匯入您自己的網格資料時,您必須反轉模型的 z 軸座標,並據此變更每個網格的索引,根據座標系統中的變更來周遊三角形。
假設我們已成功將立方體網格從右側 OpenGL ES 2.0 座標系統移至左側 Direct3D 座標系統,讓我們看看如何載入立方體資料以在這兩個模型中進行處理。
指示
步驟 1:建立輸入配置
在OpenGL ES 2.0中,您的頂點資料做為屬性提供,這些屬性將提供給著色器物件並由著色器物件讀取。 通常,您會向著色器程式物件提供一個包含著色器 GLSL 中使用的屬性名稱的字串,並取得可提供給著色器的記憶體位置。 在此範例中,頂點緩衝區物件包含自訂頂點結構的清單,其定義和格式如下:
OpenGL ES 2.0:設定包含每個頂點資訊的屬性。
typedef struct
{
GLfloat pos[3];
GLfloat rgba[4];
} Vertex;
在 OpenGL ES 2.0 中,輸入配置是隱含的;您採用通用 GL_ELEMENT_ARRAY_BUFFER 並提供步距和位移,以便頂點著色器可以在上傳資料後解釋資料。 在轉譯之前,您可以使用 glVertexAttribPointer 通知著色器哪些屬性會對應到每個頂點資料塊的哪些部分。
在 Direct3D 中,您必須提供輸入配置,以描述在建立緩衝區時頂點緩衝區中的頂點資料結構,而不是在繪製幾何之前。 若要這樣做,您可以使用輸入配置,對應至記憶體中個別頂點的資料配置。 對此進行正確指定非常重要!
您在此將輸入描述建立為 D3D11_INPUT_ELEMENT_DESC 結構的陣列。
Direct3D:定義輸入配置描述。
struct VertexPositionColor
{
DirectX::XMFLOAT3 pos;
DirectX::XMFLOAT3 color;
};
// ...
const D3D11_INPUT_ELEMENT_DESC vertexDesc[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
此輸入描述會將頂點定義為一對 2 個 3 座標向量:一個 3D 向量,以將頂點的位置儲存在模型座標中,另一個 3D 向量用來儲存與頂點相關聯的 RGB 色彩值。 在本案例中,您使用 3x32 位元浮點格式,我們在程式碼中將其元素表示為 XMFLOAT3(X.Xf, X.Xf, X.Xf)
。 每當處理著色器將使用的資料時,都應該使用 DirectXMath 程式庫中的類型,因為它可以確保該資料的正確封裝和對齊。 (例如,對向量資料使用 XMFLOAT3 或 XMFLOAT4,對矩陣使用 XMFLOAT4X4。)
如需所有可能格式類型的清單,請參閱 DXGI_FORMAT。
定義每個頂點輸入配置後,您即可建立配置物件。 在下列程式碼中,將其寫入 m_inputLayout,即 ComPtr 類型的變數 (指向 ID3D11InputLayout 類型的物件)。 fileData 包含上一步驟移植著色器中編譯的頂點著色器物件。
Direct3D:建立頂點緩衝區所使用的輸入配置。
Microsoft::WRL::ComPtr<ID3D11InputLayout> m_inputLayout;
// ...
m_d3dDevice->CreateInputLayout(
vertexDesc,
ARRAYSIZE(vertexDesc),
fileData->Data,
fileShaderData->Length,
&m_inputLayout
);
我們已定義輸入配置。 現在,讓我們建立使用此版面配置的緩衝區,並將它載入立方體網格資料。
步驟 2:建立和載入頂點緩衝區
在OpenGL ES 2.0中,您會建立一組緩衝區,一個用於位置資料,另一個用於色彩資料。 (您也可以建立同時包含和單一緩衝區的結構。) 您可以將每個緩衝區繫結,並將位置和色彩資料寫入其中。 稍後,在轉譯函式期間,再次繫結緩衝區,並提供著色器與緩衝區中的資料格式,以便正確解譯它。
OpenGL ES 2.0:繫結頂點緩衝區
// upload the data for the vertex position buffer
glGenBuffers(1, &renderer->vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, renderer->vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(VERTEX) * CUBE_VERTICES, renderer->vertices, GL_STATIC_DRAW);
在 Direct3D 中,著色器可存取的緩衝區會以 D3D11_SUBRESOURCE_DATA 結構表示。 若要將這個緩衝區的位置繫節到著色器物件,需要使用 ID3DDevice::CreateBuffer 為每個緩衝區建立一個CD3D11_BUFFER_DESC 結構,然後透過呼叫特定於緩衝區類型的設定方法來設定 Direct3D 裝置內容的緩衝區,例如 ID3DDeviceContext::IASetVertexBuffers。
當您設定緩衝區時,必須從緩衝區開頭設定步距 (個別頂點的資料元素大小) 以及位移 (頂點資料陣列實際開始的位置)。
請注意,我們將指向 vertexIndices 陣列的指標指派給 D3D11_SUBRESOURCE_DATA 結構的 pSysMem 欄位。 如果不正確,您的網格將會損毀或空白!
Direct3D:建立和設定頂點緩衝區
D3D11_SUBRESOURCE_DATA vertexBufferData = {0};
vertexBufferData.pSysMem = cubeVertices;
vertexBufferData.SysMemPitch = 0;
vertexBufferData.SysMemSlicePitch = 0;
CD3D11_BUFFER_DESC vertexBufferDesc(sizeof(cubeVertices), D3D11_BIND_VERTEX_BUFFER);
m_d3dDevice->CreateBuffer(
&vertexBufferDesc,
&vertexBufferData,
&m_vertexBuffer);
// ...
UINT stride = sizeof(VertexPositionColor);
UINT offset = 0;
m_d3dContext->IASetVertexBuffers(
0,
1,
m_vertexBuffer.GetAddressOf(),
&stride,
&offset);
步驟 3:建立和載入索引緩衝區
索引緩衝區是讓頂點著色器查閱個別頂點的有效方式。 雖然它們並非必要,但我們在此範例轉譯器中使用它們。 如同 OpenGL ES 2.0 中的頂點緩衝區,索引緩衝區會建立並繫結為一般用途緩衝區,而您稍早建立的頂點索引會複製到其中。
當您準備好繪製時,請再次繫結頂點和索引緩衝區,並呼叫 glDrawElements。
OpenGL ES 2.0:將索引順序傳送至繪製呼叫。
GLuint indexBuffer;
// ...
glGenBuffers(1, &renderer->indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderer->indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
sizeof(GLuint) * CUBE_INDICES,
renderer->vertexIndices,
GL_STATIC_DRAW);
// ...
// Drawing function
// Bind the index buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderer->indexBuffer);
glDrawElements (GL_TRIANGLES, renderer->numIndices, GL_UNSIGNED_INT, 0);
對於 Direct3D,它的程序有點非常相似,儘管更具說教性。 將索引緩衝區做為 Direct3D 子資源提供給您在設定 Direct3D 時建立的 ID3D11DeviceContext。 您可以透過使用為索引陣列配置的子資源呼叫 ID3D11DeviceContext::IASetIndexBuffer 來完成此操作,如下所示。 (再次注意,您將指向 cubeIndices 陣列的指標指派給了D3D11_SUBRESOURCE_DATA 結構的 pSysMem 欄位。)
Direct3D:建立索引緩衝區。
m_indexCount = ARRAYSIZE(cubeIndices);
D3D11_SUBRESOURCE_DATA indexBufferData = {0};
indexBufferData.pSysMem = cubeIndices;
indexBufferData.SysMemPitch = 0;
indexBufferData.SysMemSlicePitch = 0;
CD3D11_BUFFER_DESC indexBufferDesc(sizeof(cubeIndices), D3D11_BIND_INDEX_BUFFER);
m_d3dDevice->CreateBuffer(
&indexBufferDesc,
&indexBufferData,
&m_indexBuffer);
// ...
m_d3dContext->IASetIndexBuffer(
m_indexBuffer.Get(),
DXGI_FORMAT_R16_UINT,
0);
稍後,您將透過呼叫 ID3D11DeviceContext::DrawIndexed (或 ID3D11DeviceContext::Draw 對於未索引的頂點) 來繪製三角形,如下所示。 (如需詳細資訊,請跳到繪製到畫面。)
Direct3D:繪製索引頂點。
m_d3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_d3dContext->IASetInputLayout(m_inputLayout.Get());
// ...
m_d3dContext->DrawIndexed(
m_indexCount,
0,
0);
上一步
後續步驟
備註
建構 Direct3D 時,請將呼叫 ID3D11Device 上的方法的程式碼分離到需要重新建立裝置資源時呼叫的方法。 (在 Direct3D 專案範本中,此程式碼位於轉譯器物件的 CreateDeviceResource 方法中。 另一方面,更新裝置內容 (ID3D11DeviceContext) 的程式碼則放置在 Render 方法中,因為這是您實際建立著色器階段並繫結資料的地方。
相關主題