是否可以在具有或不具有几何体着色器的情况下使用相同的顶点着色器和片段着色器?

And*_*ane 3 opengl shader glsl geometry-shader

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

出于性能方面的考虑,我不想一直使用几何着色器,甚至也不想使用它,因为大多数时候大多数对象都不需要它。但是,当我确实需要它时,顶点着色器和片段着色器应该做同样的事情。我可以重用顶点和片段着色器吗?

IE浏览器

顶点:

#version 330
in vec3 position;
out vec3 whatever;

void main()
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

几何:

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

void main()
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

分段:

#version 330
in whatever

void main()
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

因此,在没有几何着色器的情况下,这是可行的,因为顶点out whatever对应于fragment in whatever。但是,使用几何体着色器,我最终要重新定义输入和输出。

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

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

有什么建议么?

Nic*_*las 5

实际上,您可以这样做。但是,您需要接口块来执行此操作。实际上,这是创建输入/输出接口块以解决的主要问题之一:

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

void main()
{
    ...
    whatever = ...;
}
Run Code Online (Sandbox Code Playgroud)

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

在片段着色器中:

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

void main()
{
    ...
    ... = whatever;
}
Run Code Online (Sandbox Code Playgroud)

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

再次注意,片段着色器将变量称为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;
}
Run Code Online (Sandbox Code Playgroud)

好,发生了很多事情。

您要注意的第一件事是,我们似乎已经两次声明了相同的接口块。不,我们没有;输入和输出接口块位于不同的名称空间中。因此,最好将一个输入接口块声明为与输出接口块相同的名字。

输入DataData顶点着色器的输出匹配。输出DataData片段着色器的输入匹配。因此接口匹配。

现在,您可能会注意到我们对这些块的声明有所不同。输入块具有标签vertex_input[],而输出块具有vertex_output。这不像在C / C ++中的struct声明之后声明的struct变量。该名称是所谓的接口块的实例名称。这个非常重要。

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

没有实例名称声明的块将全局范围内的成员。这就是为什么我们可以whatever在VS和FS中仅使用该名称进行引用的原因。但是,由于GS需要具有两个单独的whatever变量,因此我们需要某种方法来区分它们。

这就是实例名称的用途。通过为该块指定实例名称,我们必须以该实例名称为对该变量的所有引用添加前缀。

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

最后,您会注意到GS的输入变量不是数组。而是排列的接口块的实例名称。这就是接口块在GS(以及采用阵列输入/输出的镶嵌着色器)中工作的方式。

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