【问题标题】:Is it possible to have the same vertex shader and fragment shader with or without a geometry shader?是否可以有相同的顶点着色器和片段着色器,有或没有几何着色器?
【发布时间】:2016-01-15 17:43:23
【问题描述】:

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

出于性能原因,我不想一直使用几何着色器,即使作为通道,因为大多数对象大部分时间都不需要它。但是,当我确实想要它时,顶点和片段着色器应该做同样的事情。我可以重复使用我的顶点和片段着色器吗?

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,但未能找到任何使用它的示例。

有什么建议吗?

【问题讨论】:

  • "我读到您可以使用:layout (location = 0) out whatever" Taht 无法编译,因为您使用的是 GLSL 3.30。该功能是在 GL 4.2 中添加的,也是 ARB_separate_shader_object 规范的一部分。所以你可以在 3.30 中使用它,但你还需要explicitly request the use of that extension in your shader。
  • 谢谢,但您的其他答案似乎是更好的方法。

标签: opengl glsl shader geometry-shader


【解决方案1】:

您实际上可以做到这一点。但是你需要interface blocks 来做到这一点。事实上,这是创建输入/输出接口块来解决的主要问题之一:

#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),它们会正常工作。现在,是时候看看几何着色器在它们之间应该是什么样子了:

#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。实例名称​​仅在着色器中用于命名范围成员。不影响接口匹配。

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

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

【讨论】:

  • 非常感谢。我不得不添加一个;到顶点和片段着色器中的输入和输出接口块,尽管这只是一个小问题。
猜你喜欢
  • 1970-01-01
  • 2015-11-20
  • 2019-01-24
  • 2011-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多