为什么HLSL有语义?

时间:2014-02-27 09:26:21

标签: glsl hlsl

在HLSL中,我必须使用语义将信息从顶点着色器传递到片段着色器。在GLSL中,不需要语义。语义的客观利益是什么?

示例:GLSL

顶点着色器

varying vec4 foo
varying vec4 bar;

void main() {
  ...
  foo = ...
  bar = ...
}

片段着色器

varying vec4 foo
varying vec4 bar;

void main() {
  gl_FragColor = foo * bar;
}

示例:HLSL

顶点着色器

struct VS_OUTPUT
{
   float4  foo : TEXCOORD3;
   float4  bar : COLOR2;
}

VS_OUTPUT whatever()
{
  VS_OUTPUT out;

  out.foo = ...
  out.bar = ...

  return out;
}

像素着色器

void main(float4 foo : TEXCOORD3,
          float4 bar : COLOR2) : COLOR
{
    return foo * bar;
}

我看到foo中的barVS_OUTPUT与片段着色器中foo的{​​{1}}和bar的连接方式。我没有得到的是为什么我手动选择语义来携带数据。为什么像GLSL一样,DirectX只能找出放置数据的位置并在连接着色器时连接它?

手动指定语义还有一些更具体的优势,还是只是从汇编语言着色器时代遗留下来?选择说TEXCOORD4而不是COLOR2或BINORMAL1有一些速度优势吗?

我认为语义可能意味着意义,mainfoo没有任何意义但是如果bar不是TEXCOORD而且foo它们也可以模糊意义不是颜色。我们没有将语义放在C#或C ++或JavaScript变量上,为什么HLSL需要它们呢?

3 个答案:

答案 0 :(得分:12)

简单地说,(旧的)glsl使用这种变量来命名变量(请注意,变量现在已被弃用)。

语义的明显好处是,您不需要在各阶段之间使用相同的变量名称,因此DirectX管道通过语义而不是变量名称进行匹配,并且只要您具有兼容的布局,就会重新排列数据。

如果您通过foo2重命名foo,则需要在可能的所有着色器(以及最终的着色器)中替换此名称。使用语义,您不需要这个。

此外,由于您不需要完全匹配,因此可以更轻松地分离着色器阶段。

例如:

您可以使用这样的顶点着色器:

struct vsInput
{
float4 PosO : POSITION;
float3 Norm: NORMAL;
float4 TexCd : TEXCOORD0;
};

struct vsOut
{
float4 PosWVP : SV_POSITION;
float4 TexCd : TEXCOORD0;
float3 NormView : NORMAL;
};

vsOut VS(vsInput input)
{ 
     //Do you processing here
}

像这样的像素着色器:

struct psInput
{
    float4 PosWVP: SV_POSITION;
    float4 TexCd: TEXCOORD0;
};

由于顶点着色器输出提供了像素着色器所需的所有输入,因此这是完全有效的。法线将被忽略,不会提供给像素着色器。

但是,您可以交换到可能需要法线的新像素着色器,而无需另外使用Vertex Shader。您也可以只交换PixelShader,因此保存了一些API调用(Separate Shader Objects扩展名是OpenGL的等价物。)

因此,在某些方面,语义为您提供了一种在阶段之间封装输入/输出的方法,并且由于您引用其他语言,这将等同于使用属性/设置器/指针地址......

速度方面根据命名没有区别(你可以用任何你想要的方式命名语义,当然除了系统之外)。不同的布局位置确实意味着一个打击(管道将为您重新组织,但也会发出警告,至少在DirectX中)。

OpenGL还提供了Layout Qualifier,它大致相当(技术上有点不同,但是或多或少遵循相同的概念)。

希望有所帮助。

答案 1 :(得分:3)

正如Catflier上面提到的,使用语义可以帮助解耦着色器。

但是,我可以想到语义的一些缺点:

  1. 语法数量受您使用的DirectX版本的限制,因此可能会耗尽。

  2. 语义名称有点误导。你会看到很多这类变量(看看posWorld变量):

    struct v2f { float4 position : POSITION; float4 posWorld : TEXCOORD0; }

  3. 您将看到posWorld变量和TEXCOORD0语义根本不相关。但它很好,因为我们不需要将纹理坐标传递给TEXCOORD0。但是,这可能会导致那些只使用着色器语言的人之间产生一些混淆。

答案 2 :(得分:0)

与在变量前写入: NAME相比,我更喜欢在变量后写入layout(location = N) in。 另外,与Vulkan不同,如果更改顶点输入的顺序,则无需更新HLSL着色器。