3.3.2输入装配状态配置
在将适当的资源绑定到顶点和索引缓冲区槽之后,在使用输入装配程序之前,必须在输入装配程序中设置另外两个配置。
- 第一个是输入布局对象,输入装配程序使用它来知道从哪个输入槽读取逐顶点数据以构建完整的顶点。
- 第二个参数是输入装配程序应该使用的基元拓扑,以确定如何将顶点分组到基元中。
这两种状态与用于执行管道的绘制方法相结合,决定了管道如何解释顶点和基元数据。我们将详细探讨这些状态中的每一种,然后考虑它们如何与可用的绘制方法交互,以产生不同的输入配置。
1.输入布局
输入布局对象可以被认为是一个配方,它告诉输入装配程序如何创建顶点。每个顶点都由一组向量属性组成,每个属性最多有四个组件。由于最多有16个顶点缓冲区可用于绑定,输入装配序需要知道从哪里读取这些组件中的每一个,并了解将它们放在最终组装的顶点中的顺序。输入布局对象将这些信息提供给输入装配程序。
要创建输入布局对象,应用程序必须创建D3D11_Input_ELEMENT_DESC结构的数组,每个结构对应所需顶点配置的每个组件。D3D11_INPUT_ELEMENT_DESC的成员如清单3.4所示。我们将研究这些结构成员中的每一个表示什么,以及它们如何定义输入汇编程序完成其工作所需的信息。
Struct D3D11_INPUT_ELEMENT_DESC {
LPCSTR SemanticName;
UINT Semanticlndex;
DXGI_FORMAT Format;
UINT InputSlot;
UINT AlignedByteOffset;
D3D11_INPUT_CLASSIFICATI0N InputSlotClass;
UINT InstanceDataStepRate;
}
Per-vertex elements
第一个参数SemanticName标识顶点属性的文本名称,该名称必须与顶点着色器程序中提供的相应名称相匹配。顶点着色器的每个输入都必须在其HLSL源代码中定义语义。该语义名称用于将顶点着色器输入与输入汇编程序提供的顶点数据相匹配。
Semanticindex是一个允许SemanticName多次使用的整数。例如,如果在单个顶点布局中使用多组纹理坐标,则每组坐标可以使用相同的SemanticName,但会使用增加的Semanticlndex值来区分它们。
下一个结构成员是组件的Format。这指定了什么数据类型以及属性中包含多少元素。可用的格式范围从1到4个元素,同时提供浮点和整数类型。下一个参数是InputSlot,它指示应该从16个顶点缓冲区插槽中的哪一个读取该组件的数据。
AlignedByteOffset表示此D3D11_INPUT_element_DESC描述的项到顶点缓冲区中第一个元素的偏移。这会“告诉”输入装配程序在顶点缓冲区的何处开始从。
Per-instance elements
最后两个结构成员声明了关于实例化的顶点功能。实例化绘制方法本质上使用单个绘制调用将模型提交到管道。然后对该模型进行多次“实例化”,但在每个实例级别而不是每个顶点级别指定顶点数据的子集。当同一对象的多个副本必须在整个场景的不同位置渲染时,经常使用此选项。顶点格式将在其定义中包括一个变换矩阵,该矩阵仅递增到模型的每个实例的下一个值。然后,在顶点缓冲区中提供模型的所有实例的变换矩阵,并将其绑定到输入汇编程序。执行绘制调用时,顶点数据一次提交一个副本到顶点着色器,变换矩阵在模式的实例之间更新。
如果顶点组件提供每个实例的数据,则必须使用InputSlotClass参数指定该数据。在这种情况下,还可以选择只有在向管道提交了一定数量的实例后,才递增到顶点缓冲区中的下一个元素。这是在InstanceDataStepRate中指定的,并提供了对每个实例参数更改频率的粗略控制。由于这是在单个属性级别上指定的,因此可以使用不同的属性以不同的速率前进到下一个数据成员。在识别了所有所需的顶点属性并填充了一组描述结构之后,应用程序可以使用ID3DllDevice::CreateInputlayout()方法创建ID3DllInputLayout对象。
清单3.5演示了如何创建输入布局对象。
ID3D11InputLayout* m_InputLayout = NULL;
D3D11_INPUT_ELEMENT_DESC Layout[] =
{
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}
};
UINT NumElements = ARRAYSIZE(Layout);
hr = m_Device->CreateInputLayout(Layout, NumElements, g_VS, Size, &m_InputLayout);
if (FAILED(hr))
{
return ProcessFailure(m_Device, L"Failed to create input layout in OUTPUTMANAGER", L"Error", hr, SystemTransitionsExpectedErrors);
}
在这里,我们可以看到D3D11_INPUT_ELEMENT_DESC结构的数组被分配,然后从输入容器对象填充。ID3D11
Device::CreateInputLayout()的前两个参数提供了描述数组和数组中存在的元素数量。第三和第四参数分别是指向编译顶点着色器源代码所产生的着色器字节代码以及该字节代码的大小的指针。这用于将元素描述的输入数组与将用于向其提供数据的HLSL着色器进行比较。在创建输入布局时比较这两个对象可以降低着色器与特定输入布局一起使用时运行时所需的验证量。
2.基本拓扑
必须在输入装配程序中设置的最终状态是基元拓扑。我们可以将输入装配程序的工作视为两项任务——首先,它必须使用输入布局对象从提供的输入资源中装配顶点;其次,它必须将顶点流组织成基元流。每个基本体都使用一个小的顺序顶点组(其顺序由顶点缓冲区中的出现顺序或索引缓冲区中用于索引渲染的索引顺序决定)来定义组成它的组件。
基本拓扑的常见示例有三角形列表、三角形条带、直线列表和直线条带。在三角形列表的示例中,将为顶点流中的每三个顶点创建一个基本体。
基元拓扑设置告诉输入装配程序如何从装配的顶点流构建各种基元。每个基本体中使用的顶点数,以及如何从组装的顶点流中选择顶点,都是基本体拓扑设置的函数。清单3.6中提供了可用的基元类型,并演示了如何使用ID3DllDeviceContext::IASetPrimitiveTopology()方法设置基元拓扑。
enum D3D11_PRIMITIVE_T0P0L0GY {
D3Dll_PRIMITIVE_T0POL0GY_UNDEFINED,
D3D11_PRIMITIVE_T0P0L0GY_P0INTLIST,
D3D11_PRIMITIVE_T0P0L0GY_LINELIST,
D3D11_PRIMITIVE_T0P0L0GY_LINESTRIP,
D3D11_PRIMITIVE_T0P0L0GY_TRIANGLELIST,
D3D11_PRIMITIVE_T0P0L0GY_TRIANGLESTRIP,
D3D11_PRIMITIVE_T0P0L0GY_LINELIST_ADJ,
D3D11_PRIMITIVE_T0P0L0GY_LINESTRIP_ADJ,
D3D11_PRIMITIVE_T0P0L0GY_TRIANGLELIST_ADJ,
D3D11_PRIMITIVE_T0P0L0GY_TRIANGLESTRIP_ADJ,
D3D11_PRIMITIVE_T0P0L0GY_1_C0NTR0L_P0INT_PATCHLIST,
D3D11_PRIMITIVE_TOP0LOGY_2_CONTROL_POT.NT_PATCHLTST,
D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST,
D3D11_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST,
D3Dll_PRIMITIVE_TOPOLOGY_5_CONTROL_POINT_PATCHLIST,
D3D11_PRIMITIVE_T0P0L0GY_6_C0NTR0L_P0T.NT_PATCHLTST,
D3D11_PRIMITIVE_T0P0L0GY_7_C0NTR0L_P0INT_PATCHLIST,
D3D11_PRIMITIVE_T0P0L0GY_8_C0NTR0L_P0INT_PATCHLIST,
D3D11_PRIMITIVE_T0P0L0GY_9_C0NTR0L_P0INT_PATCHLIST,
D3D11_PRIMITIVE_TOPOLOGY_10_CONTROL_POINT_PATCHLIST,
D3D11_PRIMITIVE_T0P0L0GY_11_C0NTR0L_P0INT_PATCHLIST,
D3D11_PRIMITIVE_TOPOLOGY_12_CONTROL_POINT_PATCHLIST,
D3D11_PRIMITIVE_TOPOLOGY_13_CONTROL_POINT_PATCHLIST,
D3D11_PRIMITIVE_T0P0L0GY_14_C0NTR0L_P0INT_PATCHLIST,
D3Dll_PRIMITIVE_TOPOLOGY_15_CONTROL_POINT_PATCHLIST,
D3Dll_PRIMITIVE_TOPOLOGY_16_CONTROL_POINT_PATCHLIST,
D3Dll_PRIMITIVE_TOPOLOGY_17_CONTROL_POINT_PATCHLIST,
D3D11_PRIMITIVE_TOPOLOGY_18_CONTROL_POINT_PATCHLIST,
D3Dll_PRIMITIVE_TOPOLOGY_19_CONTROL_POINT_PATCHLIST,
D3D11_PRIMITIVE_TOPOLOGY_20_CONTROL_POINT_PATCHLIST,
D3Dll_PRIMITIVE_TOPOLOGY_21_CONTROL_POINT_PATCHLIST,
D3D11_PRIMITIVE_TOPOLOGY_22_CONTROL_POINT_PATCHLIST,
D3D11_PRIMITIVE_TOPOLOGY_23_CONTROL_POINT_PATCHLIST,
D3D11_PRIMITIVE_T0P0L0GY_24_C0NTR0L_P0INT_PATCHLIST,
D3Dll_PRIMITIVE_TOPOLOGY_25_CONTROL_POINT_PATCHLIST,
D3Dll_PRIMITIVE_TOPOLOGY_26_CONTROL_POINT_PATCHLIST,
D3Dll_PRIMITIVE_TOPOLOGY_27_CONTROL_POINT_PATCHLIST,
D3D11_PRIMITIVE_TOPOLOGY_28_CONTROL_POINT_PATCHLIST,
D3D11_PRIMITIVE_TOPOLOGY_29_CONTROL_POINT_PATCHLIST,
D3D11_PRIMITIVE_TOPOLOGY_30_CONTROL_POINT_PATCHLIST,
D3D11_PRIMITIVE_TOPOLOGY_31_CONTROL_POINT_PATCHLIST,
D3Dll_PRIMITIVE_TOPOLOGY_32_CONTROL_POINT_PATCHLIST
}
// Specify the type of geometry that we w i l l be dealing with.
m_pContext->IASetPrimitiveTopology( primType );
从清单3.6中可以看到,有很多可用的基元类型。如果您以前使用过Direct3D 10,则列表中的前九个条目应该看起来很熟悉,因为它们在Direct3D 11之前就已经使用过了。然而,列表的其余部分提供了一系列不同的控制点补丁列表,每个控制点补丁具有要包括在控制补丁中的不同数量的点,最多32个。引入这些基元类型是为了支持Direct3D11中引入的新镶嵌(曲面细分)阶段。为了更好地理解所有这些基元拓扑是如何组织顶点流的,我们将创建一个具有有序顶点序列的示例,然后研究每个拓扑类型如何使用该顶点流创建基元。图3.6显示了顶点的样本集,每个顶点都根据其在顶点流中的位置进行编号。
点基元
线基元
三角形基元
https://learn.microsoft.com/zh-cn/windows/win32/direct3d11/d3d10-graphics-programming-guide-primitive-topologies
https://learn.microsoft.com/zh-cn/windows/uwp/graphics-concepts/vertex-and-index-buffers
https://learn.microsoft.com/zh-cn/windows/win32/direct3d12/important-changes-from-directx-11-to-directx-12
https://learn.microsoft.com/zh-cn/windows/win32/direct3d11/d3d10-graphics-programming-guide-input-assembler-stage-getting-started