2014-02-27 21 views
8

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

实施例: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; 
} 

我看到如何在VS_OUTPUTfoobar获得片段着色器连接到foobarmain。我没有得到的是为什么我手动选择语义来携带数据。为什么像GLSL一样,DirectX不能确定在连接着色器时将数据放置在哪里并将其连接起来?

手动指定语义还有一些更具体的优点,还是仅仅从汇编语言着色器日期剩下来了吗?在COLOR2或BINORMAL1上选择TEXCOORD4是否有一些速度优势?

我得到的语义可以暗示的意思,也没有意义foobar但它们也隐晦的意思是刚刚好,如果foo不是TEXCOORD和bar并不出彩。我们不把语义放在C#或C++或JavaScript变量上,为什么他们需要HLSL?

回答

8

简单地说,(旧的)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; 
}; 

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

但是,您可以交换到可能需要法线的新像素着色器,而不需要另一个顶点着色器实现。您也可以只交换PixelShader,因此可以保存一些API调用(Separate Shader Objects扩展名为OpenGL等效项)。

因此,在某些方面的语义为您提供一种方式来封装/输出你的阶段之间,因为你参考其他语言,这将是使用性能/ setter方法/指针地址的等效...

速度明智的,取决于命名没有区别(你可以以任何你想要的方式命名语义,当然除了系统之外)。不同的布局位置意味着一个命中寿(管道将重新组织,但也会发出警告,至少在DirectX中)。 OpenGL也提供了Layout Qualifier,它大致相当(技术上有些不同,但是或多或少地遵循相同的概念)。

希望有所帮助。

2

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

不过,也有语义的一些缺点,我能想到的:

  1. 语义的数量由您使用的DirectX版本的限制,所以它可能会用完。

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

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

你会看到posWorld变量和TEXCOORD0语义是不相关的。但是没关系,因为我们不需要将纹理坐标传递给TEXCOORD0。然而,这可能会导致那些只是选择着色语言的人之间的混淆。