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

3

我正在学习几何着色器,并且我有一个用例需要使用它。

出于性能原因,即使作为一种传递方式,大多数对象大部分时间都不需要使用几何着色器,因此我不想始终使用几何着色器。但是当我需要时,顶点和片段着色器应该做相同的事情。我能重复使用我的顶点和片段着色器吗?

例如:

顶点着色器:

#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。但是使用几何着色器后,我不得不将whatever重新定义为in和out。

我读到可以使用:layout(location = 0)out whatever,然后您不需要相同的名称,但这对我不起作用,并导致编译错误:ERROR:-1:65535:'' :存储限定符与布局限定符id无效。我认为这是由于我没有足够新的OpenGL版本支持该语法。

我也读到可以使用扩展:arb_separate_shader_objects,但未找到任何示例。

有什么建议吗?


我看到你可以使用:layout (location = 0) out whatever,但这在GLSL 3.30中不会编译通过。该功能是在GL 4.2中添加的,并且也是ARB_separate_shader_object规范的一部分。因此,您可以在3.30中使用它,但还需要在着色器中显式请求使用该扩展 - Nicol Bolas
谢谢,但是你的另一个答案似乎是更好的方法。 - Andrew Parlane
1个回答

9
你实际上可以做到这一点。但是你需要使用 接口块 来完成。事实上,这正是输入/输出接口块被创建的主要问题之一:
#version 330
in vec3 position;
out Data
{
  vec3 whatever;
};

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

这是您的顶点着色器,使用接口块作为其输出。顶点着色器输入不能被聚合到接口块中。请注意,顶点着色器调用接口块成员whatever。很快这将非常重要。
在您的片段着色器中:
#version 330
in Data
{
    in vec3 whatever;
};

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

片段着色器现在声明了一个补充的输入块。为了让它起作用,该块必须使用与前一阶段相应输出块相同的名称。而且它必须按照相应输出块相同的顺序声明全部相同的变量。
请注意,片段着色器将变量称为whatever。这很重要。
如果您将这两个着色器链接在一起(直接或间接地使用独立程序),它们将正常工作。现在,是时候看看几何着色器需要什么样才能适合它们之间了:
#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++中在结构声明后声明结构变量。这个名称被称为接口块的实例名称,这非常重要。

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

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

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

请注意,跨接口的块是通过块名称匹配的。也就是说,GS的输入与VS的输出匹配,因为它们都命名为Data。实例名称在着色器内用于命名空间成员。它不影响接口匹配。

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

在这个定义下,您可以在VS和FS之间插入这个GS,而无需修改任何一个。所以你不需要修改VS或FS代码(除了使用接口块之外)。


非常感谢。我不得不在顶点和片段着色器的输入和输出接口块中添加一个分号,尽管这只是一个小问题。 - Andrew Parlane

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接