D3D12渲染技术之顶点着色器

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jxw167/article/details/82661497

相信大家以前用过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_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’.

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

阅读更多

扫码向博主提问

海洋_

博客专家

非学,无以致疑;非问,无以广识
  • 擅长领域:
  • 3D引擎架构
  • 服务器架构
  • GPU渲染
  • 客户端架构
  • 引擎优化
去开通我的Chat快问
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页