是否可以使用或不使用几何着色器来使用相同的顶点着色器和片段着色器?

Is it possible to have the same vertex shader and fragment shader with or without a geometry shader?

所以我只是在学习几何着色器,我有一个用例。

出于性能方面的原因,我不想一直使用几何着色器,即使是作为传递,因为大多数对象大多数时候不需要它。然而,当我确实需要它时,顶点和片段着色器应该做同样的事情。我可以重复使用我的顶点和片段着色器吗?

IE.

顶点:

#version 330
in vec3 position;
out vec3 whatever;

void main()
{
    ...
}

几何:

#version 330
layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;
in whatever[];
out whatever;

void main()
{
    ...
}

片段:

#version 330
in whatever

void main()
{
    ...
}

因此,如果没有几何着色器,这也是可行的,因为顶点 out whatever 对应于片段 in whatever。然而,对于几何着色器,我最终将任何东西重新定义为输入和输出。

我读到你可以使用:layout (location = 0) out whatever 然后你不需要相同的名称,但这对我不起作用,给出编译错误:ERROR: -1:65535: '' : storage qualifier not valid with layout qualifier id。我认为这是因为我没有足够新的 opengl 版本来支持该语法。

我还读到您可以使用扩展名:arb_separate_shader_objects,但找不到任何使用它的示例。

有什么建议吗?

你实际上可以做到这一点。但是你需要 interface blocks 才能做到。实际上,这是创建接口块以解决的主要问题之一 input/output:

#version 330
in vec3 position;
out Data
{
  vec3 whatever;
};

void main()
{
    ...
    whatever = ...;
}

这是您的顶点着色器,使用接口块作为其输出。顶点着色器输入 不能 聚合到接口块中。注意顶点着色器调用接口块的成员whatever。这很快就会很重要。

在你的片段着色器中:

#version 330
in Data
{
    in vec3 whatever;
};

void main()
{
    ...
    ... = whatever;
}

片段着色器现在声明了一个补充输入块。为了使其工作,该块必须使用与前一阶段的相应输出块相同的名称。并且它必须声明 所有 相同的变量作为相应的输出块,以相同的顺序。

再次注意片段着色器将变量引用为whatever。目前这很重要。

如果您使用这两个着色器并将它们链接在一起(直接或间接使用 separate programs),它们将工作正常。现在,是时候看看 Geometry Shader 必须是什么样子才能适应它们:

#version 330
layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;

in Data
{
    vec3 whatever;
} vertex_input[];

out Data
{
    vec3 whatever;
} vertex_output;

void main()
{
    ...
    vertex_output.whatever = vertex_input[0].whatever;
}

好的,刚刚发生了 很多 的事情。

您首先会注意到我们似乎已经声明了同一个接口块两次。不,我们没有;输入和输出接口块位于不同的命名空间中。所以声明一个输入接口块与输出接口块同名是完全没问题的。

输入 Data 与顶点着色器的输出 Data 匹配。输出 Data 与片段着色器的输入 Data 匹配。所以接口匹配。

现在,您可能会注意到我们以不同方式声明了这些块。输入块有一个标签vertex_input[],而输出块有vertex_output。这不像在 C/C++ 中的结构声明之后声明结构变量。这个名字就是所谓的接口块的instance name。这很重要。

为什么?因为它允许我们限定接口块成员的名称。

没有实例名称声明的块在全局范围内作用于它们的成员。这就是为什么我们可以仅使用该名称在 VS 和 FS 中引用 whatever 的原因。然而,由于 GS 需要有两个独立的 whatever 变量,我们需要一些方法来区分它们。

这就是实例名称的用途。通过给块一个实例名称,我们必须在所有对该变量的引用前加上该实例名称。

请注意,跨接口的块由 块名称 匹配。也就是说,GS 的输入与 VS 的输出相匹配,因为它们都被命名为 Data。实例名称 在着色器中用于 name-scope 成员。不影响接口匹配

最后,您会注意到 GS 的输入变量不是数组。相反,它是排列的接口块的 实例名称 。这就是界面块在 GS 中的工作方式(以及采用阵列 inputs/outputs 的曲面细分着色器)。

鉴于此定义,您可以在 VS 和 FS 之间滑动此 GS,而无需更改它们中的任何一个。所以你根本不需要修改 VS 或 FS 代码(显然除了使用接口块)。