在上一篇《顶点&索引数据传入GPU》中我们给出了顶点数据的布局:
td::vector<D3D12_INPUT_ELEMENT_DESC> mInputLayout;
mInputLayout =
{
//语义,语义索引,格式,槽,偏移值,数据类型,是否实例化
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
};
这就好像一份说明书,定义了原材料的名称(语义),格式,存放位置,使用方式等,方便我们把数据运送到GPU流水线上进行对号入座:
我们在CPU端编程的时候,将顶点数据存入结构体,然后还要准备这个数据的说明书:顶点布局。它的基本结构如下,通过记录布局数组首地址和长度来定义:
typedef struct D3D12_INPUT_LAYOUT_DESC
{
D3D12_INPUT_ELEMENT_DESC *pInputElementDescs;
UINT NumElements;
} D3D12_INPUT_LAYOUT_DESC;
这里边放对顶点数据的结构描述D3D12_INPUT_ELEMENT_DESC *pInputElementDescs数组;以及顶点数据可分解为多少个描述。D3D12_INPUT_ELEMENT_DESC 也就是说可拆解为坐标,颜色,法线等单独描述的独立语义,用法如下。
mInputLayout =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
};
opaquePsoDesc.InputLayout = { mInputLayout.data(), (UINT)mInputLayout.size() };
比如这里,具有POSITION和COLOR两个数据,最终是传递给PSO的描述结构,用于创建PSO:
ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&opaquePsoDesc, IID_PPV_ARGS(&mPSOs["opaque"])));
这就相当于我们把说明书给流水线了,以后流水线中的要利用我们的顶点数据的时候就会自动去看说明书,比如顶点着色器,它的输入里边就有POSITION,其实就是建立了同语义的这样一种映射,GUP就会把顶点数据填入输入参数。
注意事项和总结:
1.要保证数据传输的正确,是基于“语义”的;无论是从CPU到流水线,还是流水线内部;
2.带有SV_标记的语义意思是系统值,我接触过的有SV_POSITION和SV_TARGET,一个是表示传递给剪裁进行透视除法的坐标位置,一个是像素着色的标记,注意SV_POSITION可以出现在顶点着色器或者几何着色器,如果有几何着色器,顶点就不处理。
3.虽然COLOR这类看起来是有含义的,但是其实是种数据结构,也就是说只要数据的结构能对上,语义可以混用(SV_不行)。