写入某些片段位置但不写入其他片段位置

Write to some fragment locations but not others

目前,我有两个着色器旨在处理相同类型的对象,但产生不同的输出:一种颜色用于屏幕,另一种选择信息。

绘制着色器的输出:

layout(location = 0) out vec4 outColor;

选择着色器的输出:

layout(location = 0) out vec4 selectionInfo0;
layout(location = 1) out ivec4 selectionInfo1;

我正在考虑将着色器组合在一起(我的应用程序中的这两个和其他着色器)以便于维护(为什么要编辑两个着色器,而你可以编辑一个着色器?)。

统一着色器的输出:

layout(location = 0) out vec4 outColor;
layout(location = 1) out vec4 selectionInfo0;
layout(location = 2) out ivec4 selectionInfo1;

在这个方案下,我会设置一个uniform来确定需要写入哪些片段。

我可以写入某些片段位置而不是其他片段位置吗?

void main()
{
    if(Mode == 1){
        outColor = vec4(1, 0, 0, 1);
    }
    else {
        selectionInfo0 = vec4(0.1, 0.2, 0.3, 0.4);
        selectionInfo1 = ivec4(1, 2, 3, 4);
    }
}

这是一种合法的方法吗?有什么需要注意的吗?

Is this a legitimate approach?

这取决于你如何定义"legitimate"。可以做成function.

片段要么是 discarded 完整的 ,要么不是。如果它被丢弃,那么该片段(大部分)没有效果。如果它没有被丢弃,那么它的所有输出要么有定义的值(即:你写给它们),要么有未定义的值。

但是,未定义的值也可以,具体取决于其他状态。例如,frambuffer's draw buffer state routes FS output colors to actual color attachments. It can also route them to GL_NONE, which throws them away. Similarly, you can use color write masks 在每个附件的基础上,关闭对您不想写入的附件的写入。

但这意味着您无法根据每个片段来确定它。您只能使用着色器外部的状态来确定这一点。 FS 不能使这发生或不发生;它必须在状态更改的绘制调用之间完成。

如果 Mode 是某种 uniform 值,那么应该没问题。但如果它是基于每个顶点或每个片段派生的东西,那么这将无法有效地工作。

至于分支性能,同样取决于 Mode。如果是 uniform,您根本不必担心。现代 GPU 可以很好地处理这个问题。如果是其他原因...好吧,您的整个方案由于已经详述的原因而停止工作,所以没有理由担心它 ;)

综上所述,我建议不要使用这种复杂性。从驾驶员的角度来看,这是一种令人困惑的处理方式。此外,由于您依赖很多其他应用程序不依赖的东西,因此您很容易遇到驱动程序错误。您的想法不同于传统的 Ubershader,因为您的选项从根本上改变了渲染目标和输出的性质。

所以我建议您尝试以尽可能常规的方式做事。如果你真的想尽量减少你使用的单独文件的数量,使用#ifdefs,并简单地用#define修补着色器字符串,基于你加载它的原因。所以你有一个着色器文件,但是有 2 个程序是从它构建的。