在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
中的bar
和VS_OUTPUT
与片段着色器中foo
的{{1}}和bar
的连接方式。我没有得到的是为什么我手动选择语义来携带数据。为什么像GLSL一样,DirectX只能找出放置数据的位置并在连接着色器时连接它?
手动指定语义还有一些更具体的优势,还是只是从汇编语言着色器时代遗留下来?选择说TEXCOORD4而不是COLOR2或BINORMAL1有一些速度优势吗?
我认为语义可能意味着意义,main
或foo
没有任何意义但是如果bar
不是TEXCOORD而且foo
它们也可以模糊意义不是颜色。我们没有将语义放在C#或C ++或JavaScript变量上,为什么HLSL需要它们呢?
答案 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上面提到的,使用语义可以帮助解耦着色器。
但是,我可以想到语义的一些缺点:
语法数量受您使用的DirectX版本的限制,因此可能会耗尽。
语义名称有点误导。你会看到很多这类变量(看看posWorld变量):
struct v2f {
float4 position : POSITION;
float4 posWorld : TEXCOORD0;
}
您将看到posWorld变量和TEXCOORD0语义根本不相关。但它很好,因为我们不需要将纹理坐标传递给TEXCOORD0。但是,这可能会导致那些只使用着色器语言的人之间产生一些混淆。
答案 2 :(得分:0)
与在变量前写入: NAME
相比,我更喜欢在变量后写入layout(location = N) in
。
另外,与Vulkan不同,如果更改顶点输入的顺序,则无需更新HLSL着色器。