在前几篇博客中,我们主要关注渲染管道的概念和数学方面。 反过来,从本篇博客开始重点介绍配置渲染管道,定义顶点和像素着色器以及将几何图形提交到渲染管道以进行绘制所需的Direct3D API接口和方法。 学习配置渲染管道,对于学习Unity的自定义渲染管线有很大帮助,它们的原理类似的,做到举一反三。
- 学习目标:
1、掌握用于定义,存储和绘制几何数据的Direct3D接口方法。
2、学习如何编写基本顶点和像素着色器。
3、了解如何使用管道状态对象配置渲染管道。
4、了解如何创建常量缓冲区数据并将其绑定到管道。
- 顶点和输入布局
Direct3D中的顶点可以包含除空间位置之外的其他数据, 要创建自定义顶点格式,我们首先创建一个包含选择的顶点数据的结构。 例如,下面说明了两种不同的顶点格式; 一个由位置和颜色组成,第二个由位置,法线向量和两组2D纹理坐标组成。
struct Vertex1
{
XMFLOAT3 Pos;
XMFLOAT4 Color;
};
struct Vertex2
{
XMFLOAT3 Pos;
XMFLOAT3 Normal;
XMFLOAT2 Tex0;
XMFLOAT2 Tex1;
};
一旦我们定义了顶点结构,需要为Direct3D提供顶点结构的描述,此描述以输入布局描述的形式提供给Direct3D,由D3D12_INPUT_LAYOUT_DESC结构表示:
typedef struct D3D12_INPUT_LAYOUT_DESC
{
const D3D12_INPUT_ELEMENT_DESC *pInputElementDescs;
UINT NumElements;
} D3D12_INPUT_LAYOUT_DESC;
输入布局描述仅仅是D3D12_INPUT_ELEMENT_DESC元素的数组,以及数组中元素的数量。
D3D12_INPUT_ELEMENT_DESC数组中的每个元素描述并对应顶点结构中的一个组件。 因此,如果顶点结构有两个组件,那么相应的D3D12_INPUT_ELEMENT_DESC数组将有两个元素。 D3D12_INPUT_ELEMENT_DESC结构定义为:
typedef struct D3D12_INPUT_ELEMENT_DESC
{
LPCSTR SemanticName;
UINT SemanticIndex;
DXGI_FORMAT Format;
UINT InputSlot;
UINT AlignedByteOffset;
D3D12_INPUT_CLASSIFICATION InputSlotClass;
UINT InstanceDataStepRate;
} D3D12_INPUT_ELEMENT_DESC;
看看下图中我们定义的D3D12_INPUT_ELEMENT_DESC与Shader的关系如下所示:
我们在声明数据结构时也要考虑它们在内存中的布局,记得刚毕业时,面试题就有关于这方面的,在这里也是跟读者回顾一下,在下面的顶点结构中,元素Pos具有0字节的偏移,因为它的开始与顶点结构的开始位置是一致的; Normal元素有一个12字节的偏移量,因为我们必须跳过Pos的字节才能达到Normal; 元素Tex0有一个24字节的偏移量,因为我们需要跳过Pos和Normal的字节来到Tex0; 元素Tex1有一个32字节的偏移量,因为我们需要跳过Pos,Normal和Tex0的字节来到Tex1。
struct Vertex2
{
XMFLOAT3 Pos; // 0-byte offset
XMFLOAT3 Normal; // 12-byte offset
XMFLOAT2 Tex0; // 24-byte offset
XMFLOAT2 Tex1; // 32-byte offset
};
相对应的输入描述如下所示:
D3D12_INPUT_ELEMENT_DESC desc1[] =
{
{
"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,
D3D12_INPUT_PER_VERTEX_DATA, 0},
{
"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12,
D3D12_INPUT_PER_VERTEX_DATA, 0}
};
D3D12_INPUT_ELEMENT_DESC desc2[] =
{
{
"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,
D3D12_INPUT_PER_VERTEX_DATA, 0},
{
"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12,
D3D12_INPUT_PER_VERTEX_DATA, 0},
{
"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24,
D3D12_INPUT_PER_VERTEX_DATA, 0}
{
"TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT, 0, 32,
D3D12_INPUT_PER_VERTEX_DATA, 0}
};
- 顶点缓冲区
为了让GPU访问顶点数组,需要将它们放在称为缓冲区的GPU资源(ID3D12Resource)中, 我们调用一个缓冲区来存储顶点顶点缓冲区。 缓冲区比纹理更简单; 它们不是多维的,并且没有mipmap,过滤器或多重采样支持。 每当我们需要为GPU提供一系列数据元素(如顶点)时,我们都会使用缓冲区。
我们通过填写描述缓冲区资源的D3D12_RESOURCE_DESC结构,然后