欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

D3D12渲染技术之顶点着色器

程序员文章站 2022-07-13 09:10:35
...

相信大家以前用过D3D9的或是编写过Shader的对顶点着色器都比较了解,现在我们回顾一下:

cbuffer cbPerObject : register(b0)
{
  float4x4 gWorldViewProj; 
};

void VS(float3 iPosL : POSITION, 
    float4 iColor : COLOR, 
    out float4 oPosH : SV_POSITION,
    out float4 oColor : COLOR)
{
  // Transform to homogeneous clip space.
  oPosH = mul(float4(iPosL, 1.0f), gWorldViewProj);

  // Just pass vertex color into the pixel shader.
  oColor = iColor;
}

Shader的编程语言我们称为HLSL(the high level shading language),语法跟C++类似,比较容易学习。我们教HLSL和编程着色器的方法将以示例为基础, 也就是说,随着博客的进展,我们将介绍需要的任何新的HLSL概念,以便实现Demo的演示, 着色器脚本通常用基于文本的文件编写,扩展名为.hlsl。

顶点着色器是名为VS的函数, 请注意,可以为顶点着色器指定任何有效的函数名称, 该顶点着色器有四个参数; 前两个是输入参数,后两个是输出参数(由out关键字表示)。 HLSL没有引用或指针,因此要从函数返回多个值,需要使用结构或输出参数, 在HLSL中,函数始终内联。

前两个输入参数构成顶点着色器的输入签名,并对应于我们用于绘制的自定义顶点结构中的数据成员。 参数语义“:POSITION”和“:COLOR”用于将顶点结构中的元素映射到顶点着色器输入参数,如下图所示。

D3D12渲染技术之顶点着色器
每个顶点元素都有一个由D3D12_INPUT_ELEMENT_DESC数组指定的相关语义, 顶点着色器的每个参数也具有附加的语义, 语义用于将顶点元素与顶点着色器参数进行匹配。

输出参数也有附加的语义(“:SV_POSITION”和“:COLOR”),这些用于将顶点着色器输出映射到下一级的相应输入(几何着色器或像素着色器)。 请注意,SV_POSITION语义是特殊的(SV代表系统值), 它用于表示在裁剪空间中保存顶点位置的顶点着色器输出元素。 我们必须将SV_POSITION语义附加到位置输出,因为GPU需要知道这个值,因为它涉及其他属性不涉及的操作,例如裁剪,深度测试和光栅化, 非系统值的输出参数的语义名称可以是任何命名有效的语义名称。
第一行通过乘以4×4矩阵gWorldViewProj将顶点位置从局部空间转换为均匀裁剪空间:

oPosH = mul(float4(iPosL, 1.0f), gWorldViewProj);

使用构造函数语法float4(iPosL,1.0f)构造一个4D向量,相当于float4(iPosL.x,iPosL.y,iPosL.z,1.0f); 因为我们知道顶点的位置是点而不是矢量,所以我们在第四个分量中放置1(w = 1)。 float2和float3类型分别代表2D和3D向量。 矩阵变量gWorldViewProj存在于所谓的常量缓冲区中,这将在后面讨论。 内置函数mul用于向量矩阵乘法。 顺便提一下,对于不同大小的矩阵乘法,mul函数是重载的; 例如,可以使用它来乘以两个4×4矩阵,两个3×3矩阵,或1×3矢量和3×3矩阵。 着色器主体中的最后一行只是将输入颜色复制到输出参数,以便将颜色输入到管道的下一个阶段:

oColor = iColor;

我们可以使用返回类型和输入签名的结构(而不是长参数列表)重写上面的顶点着色器:

cbuffer cbPerObject : register(b0)
{
  float4x4 gWorldViewProj; 
};

struct VertexIn
{
  float3 PosL : POSITION;
  float4 Color : COLOR;
};

struct VertexOut
{
  float4 PosH : SV_POSITION;
  float4 Color : COLOR;
};

VertexOut VS(VertexIn vin)
{
  VertexOut vout;

  // Transform to homogeneous clip space.
  vout.PosH = mul(float4(vin.PosL, 1.0f), gWorldViewProj);

  // Just pass vertex color into the pixel shader.
  vout.Color = vin.Color;

  return vout;
  }

注意:
如果没有几何着色器(后面介绍几何着色器),则顶点着色器必须使用SV_POSITION语义输出到裁剪空间中的顶点位置,因为这是硬件在离开顶点时期望顶点所在的空间 着色器(如果没有几何着色器)。 如果存在几何着色器,则输出裁剪空间位置可以推迟到几何着色器。
顶点着色器(或几何着色器)不执行透视处理, 它只是投影矩阵部分。,透视处理将在稍后由硬件完成。

在管道的顶点属性之间存在链接,该属性由输入布局描述定义。 如果输入的顶点不提供顶点着色器所需的所有输入,则会产生错误。 例如,以下顶点着色器输入签名和顶点数据不兼容:

//--------------
// C++ app code
//--------------
struct Vertex
{
  XMFLOAT3 Pos;
  XMFLOAT4 Color;
};

D3D12_INPUT_ELEMENT_DESC desc[] =
{
  {"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}
};

//--------------
// Vertex shader
//--------------
struct VertexIn
{
  float3 PosL  : POSITION;
  float4 Color : COLOR;
  float3 Normal : NORMAL;
};

struct VertexOut
{
  float4 PosH : SV_POSITION;
  float4 Color : COLOR;
};

VertexOut VS(VertexIn vin) { … }
当我们创建一个ID3D12PipelineState对象时,我们必须指定输入布局描述和顶点着色器。 然后,Direct3D将验证输入布局描述和顶点着色器是否兼容。
顶点数据和输入签名不需要完全匹配, 所需要的是顶点数据提供顶点着色器所期望的所有数据。 因此,允许顶点数据提供顶点着色器不使用的附加数据。 也就是说,以下是兼容的:

//--------------
// C++ app code
//--------------
struct Vertex
{
  XMFLOAT3 Pos;
  XMFLOAT4 Color;
  XMFLOAT3 Normal;
};

D3D12_INPUT_ELEMENT_DESC desc[] =
{
 {"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},
  { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 28, 
    D3D12_INPUT_PER_VERTEX_DATA, 0 }
};

//--------------
// Vertex shader
//--------------
struct VertexIn
{
  float3 PosL  : POSITION;
  float4 Color : COLOR;
};

struct VertexOut
{
  float4 PosH : SV_POSITION;
  float4 Color : COLOR;
};
 VertexOut VS(VertexIn vin) { … }

现在考虑的是顶点结构和输入签名具有匹配的顶点元素的情况,但颜色属性的类型是不同的:

//--------------
// C++ app code
//--------------
struct Vertex
{
  XMFLOAT3 Pos;
  XMFLOAT4 Color;
};

D3D12_INPUT_ELEMENT_DESC desc[] =
{
  {"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}
};

//--------------
// Vertex shader
//--------------
struct VertexIn
{
 float3 PosL  : POSITION;
  int4 Color : COLOR;
};

struct VertexOut
{
  float4 PosH : SV_POSITION;
  float4 Color : COLOR;
};

VertexOut VS(VertexIn vin) { … }

这实际上是合法的,因为Direct3D允许重新解释输入寄存器中的位。 但是,VC ++调试输出窗口提供以下警告:
D3D12 WARNING: ID3D11Device::CreateInputLayout: The provided input signature expects to read an element with SemanticName/Index: ‘COLOR’/0 and component(s) of the type ‘int32’. However, the matching entry in the Input Layout declaration, element[1], specifies mismatched format: ‘R32G32B32A32_FLOAT’.

这不是错误。。。。。。。。。

相关标签: D3D12