首先声明,本人是自学DX12,有很多的理解也许不到位,不过都是自己的理解。在很长一段时间里边,我从迷茫到有一天开始能看懂,现在是第三次开始刷DX12了,于是在此表明写作的初衷:
1.有一些DX12的学习心得,希望发出来,有大佬如果愿意指教,万分感谢;
2.如果对于才入门的人来说,这可能是我的白话教程,也许会对你有所帮助,但不可尽信,因为我也不确定我对不对;
3.DX12的概念很多,也是想把这作为自己的学习笔记来做,希望对自己也有帮助,如果有一天我发现哪里错了会及时回来更正。
那么话不多说,现在开始!!!
在上一篇《顶点&索引数据传入GPU》中我们给出了顶点数据的布局:
td::vector
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;以及顶点数据可分解为多少个描述。也就是说可拆解为坐标,颜色,法线等单独描述的个数。
opaquePsoDesc.InputLayout = { mInputLayout.data(), (UINT)mInputLayout.size() };
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 },
};
比如这里,具有POSITION和COLOR两个数据,第一个参数就是具体数据是什么,第二个就是一共包含几个数据。
这是一个说明书,对应顶点结构体的数据,将第一个数据解释为POSITION,第二个相对第一个内存偏移12,解释为COLOR。这就一一对上了,然后这个布局会被Pipeline利用起来,这是后话了,不过可以看看:
opaquePsoDesc.InputLayout = { mInputLayout.data(), (UINT)mInputLayout.size() };
ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&opaquePsoDesc, IID_PPV_ARGS(&mPSOs["opaque"])));
这就相当于我们把说明书给流水线了,以后流水线中的员工要利用我们的顶点数据的时候就会自动去看说明书,比如顶点着色器这个数学天才(见上图),它的输入里边也有POSITION,其实就是建立了同语义的这样一种映射,GUP就会把顶点数据填入输入参数。
这里我们可以提一下一些注意事项和总结:
1.要保证数据传输的正确,是基于“语义”的;(好想笑,dddd)无论是从CPU到流水线,还是流水线内部;
2.带有SV_标记的语义意思是系统值,我接触过的有SV_POSITION和SV_TARGET,一个是表示传递给剪裁进行透视除法的坐标位置,一个是像素着色的标记,注意SV_POSITION可以出现在顶点着色器或者几何着色器,如果有几何着色器,顶点就不处理。
3.虽然COLOR这类看起来是有含义的,但是其实是重数据结构,也就是说只要数据的结构能对上,语义可以混用(SV_不行)。
目前为止我们还是没有写shader,因为准备工作还没有完成,比如这个部分,解释了数据解析的说明书怎么获得。