在OpenGL中使用多个着色器

3

有没有一种方法可以创建多个着色器(包括顶点、片段,甚至几何和细分),并将它们组合起来实现功能?

例如:我已经看到了在OpenGL的较新版本中使用inout关键字的许多用途,并将使用它们来说明我的问题。

有没有一种方法可以给定一个着色器(不管是哪个,但假设是片段着色器),例如

in inVar;
out outVar;
void man(){
    var varOne = inVar;
    var varTwo = varOne;
    var varThr = varTwo;
    outVar = varThr;
}

将其转换为片段着色器。
in inVar;
out varOne;
void main(){
    varOne = inVar;
}

跟着片段着色器。
in varOne;
out varTwo;
void main(){
    varTwo = varOne;
}

跟着片段着色器。
in varTwo(
out varThr;
void main(){
    varThr = varTwo
}

最后是片元着色器。
in varThr;
out outVar;
void main(){
    outVar = varThr;
}

“in”和“out”是描述这种行为的正确“概念”吗?还是我应该寻找其他关键词?请注意,保留HTML标签。

一旦创建,glUseProgram(pgmID) 允许您在程序之间切换。 - undefined
@j-p:你是什么意思?我并不是想要切换程序。我是想要将同一类型的多个着色器的效果结合起来。 - undefined
GL中有一些“子程序”可以实现组合...https://www.opengl.org/wiki/Shader_Subroutine - undefined
2个回答

10

在OpenGL中,可以将多个相同类型的着色器附加到程序对象中。但在OpenGL ES中,这种方法不被支持。

这是来自于OpenGL 4.5规范第7.3节“程序对象”,第88页(强调添加):

多个相同类型的着色器对象可以附加到单个程序对象上,一个着色器对象也可以附加到多个程序对象上。

与此相比,OpenGL ES 3.2规范第7.3节“程序对象”,第72页如下(强调添加):

不允许将多个相同类型的着色器对象附加到单个程序对象上。然而,一个着色器对象可以附加到多个程序对象上。

然而,即使在OpenGL中,使用多个相同类型的着色器也不能像您在问题中概述的那样工作。只有其中一个着色器可以有一个main()函数。其他着色器通常只包含函数。因此,您将多个相同类型的着色器链接成管道的想法在这种形式下将无法工作。

有时候会这样使用:有一个(或多个)着色器包含了一组通用函数,只需要编译一次,就可以用于多个着色器程序。这类似于一般编程中的函数库。您可以称之为“库着色器”,虽然这个术语并不是官方术语。

然后,每个着色器程序都有一个包含main()函数的每种类型的单个着色器,该着色器包含该程序的特定逻辑和控制流程,并从“库着色器”调用通用函数。

按照您问题中的概述,您可以通过在每个着色器中定义一个函数来实现类似的功能。例如,第一个片段着色器可以包含以下代码:

void shaderOne(in vec4 varIn, out vec4 varOut) {
    varOut = varIn;
}

第二个着色器:

void shaderTwo(in vec4 varIn, out vec4 varOut) {
    varOut = varIn;
}

第三个着色器:

void shaderThr(in vec4 varIn, out vec4 varOut) {
    varOut = varIn;
}

您需要一个包含main()的着色器,您可以在其中链接其他着色器:

in vec4 varOne;
out vec4 outVar;

void main() {
    vec4 varTwo, varThr;
    shaderOne(varOne, varTwo);
    shaderTwo(varTwo, varThr);
    shaderThr(varThr, outVar);
}

我更关心的是从客户端启用/禁用单个着色器(选择应用哪些)。按照您描述的方式,需要添加一堆uniform变量,并在每个着色器调用前加上一个if/then语句。而且,我认为如果禁用shaderTwo调用,可能会影响到shaderThr。简而言之,我想知道是否可以修改您给出的代码示例,以便禁用shaderCalls,从而改变效果,但在调用后续着色器时不会出错。 - undefined
1
我不知道OpenGL是否支持在单个程序对象上使用多个相同类型的着色器。你能指出这个特性吗?据我所知,OpenGL最多支持传递多个字符串,但这些字符串只是简单地连接在一起。没有理由不自己连接它们,这也是WebGL所要求的。 - undefined
@gman 我在帖子的顶部附近添加了规范引用。我检查了2.1规范以来的历史,一直都是这样的。我见过很多成功的游戏中使用了这种方式。 - undefined
2
谢谢。所以如果我理解正确的话,它基本上没有任何意义。它在功能上等同于字符串连接和单个着色器。你可能会从中获得的唯一好处就是对小片段进行预编译,但我怀疑没有驱动程序实际上会这样做。难怪它在ES中被移除了。 - undefined
@gman 是的,我认为节省编译时间是唯一真正的好处,与连接字符串相比。除此之外,根据软件的架构,它可能更加方便。由于它被主要游戏使用,我猜他们看到了好处。包含函数的共享着色器可能相当大。 - undefined
@gman 对的,我猜字符串拼接是一个不错的思路,但需要补充一点,你在拼接的变量可能有值,也可能为空,即长度为零或只有一个空格。这可能会改变最终结果的字符串。 - undefined

4

生成着色器。这就是几乎所有3D引擎和游戏引擎所做的事情。

换句话说,它们使用代码来操作文本,并在运行时或构建时生成源代码。

有时它们会使用GLSL的预处理器。例如:

#ifdef USE_TEXTURE
  uniform sampler2D u_tex;
  varying vec2 v_texcoord;
#else
  uniform vec4 u_color;
#endif

void main() {
  #ifdef USE_TEXTURE
    gl_FragColor = texture2D(u_tex, v_texcoord);
  #else 
    gl_FragColor = u_color;
  #endif
}

然后在运行时,添加一个字符串来定义USE_TEXTURE

JavaScript

var prefix = useLighting ? '#define USE_TEXTURE' : '';
var shaderSource = prefix + originalShaderSource;

大多数引擎通过获取许多小的GLSL代码块并将它们与各种字符串替换组合来进行更多字符串操作。


但是预处理器不允许更改应用哪些着色器或者它们的顺序。它们在实际着色器编译之后应用。我的主要目标是在客户端程序和各个着色器编译完成之后(通过输入选择)执行这些操作。 - undefined
这不是着色器的工作方式。生成你的着色器。是否使用预处理器来帮助完成这个任务,取决于你自己。 - undefined

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