重要的应用程序接口(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 坐标向量对:一个三维向量用于将顶点的位置存储在模型坐标中,另一个三维向量用于存储与顶点关联的 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:创建并加载顶点缓冲区(s)
在 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结构,然后通过调用特定于缓冲区类型的 set 方法(如 ID3DDeviceContext::IASetVertexBuffers)来设置 Direct3D 设备上下文的缓冲区。
设置缓冲区时,必须从缓冲区的开头设置步幅(单个顶点的数据元素的大小)以及偏移量(顶点数据数组实际开始的位置)。
请注意,我们将指向 顶点索引 数组的指针分配给 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 方法中,因为这是实际构造着色器阶段并绑定数据的位置。
相关主题