OpenGL GLSL统一分支与多个着色器的比较

8
我是一名辅助翻译,以下是您需要翻译的内容:我已经阅读了许多有关统一if语句的文章,这些文章处理分支以更改大型着色器“超级着色器”的行为。我开始使用超级着色器(opengl lwjgl),但后来意识到,在片段着色器中添加由uniform设置的if语句进行简单计算的简单操作,与不带uniform if语句的分开的着色器相比,我的fps降低了5个。我没有为我的fps限制设置任何上限,它只是尽可能快地刷新。我将要添加法线映射和视差映射,我可以看到两条路线:

超级顶点着色器:

#version 400 core

layout(location = 0) in vec3 position;
layout(location = 1) in vec2 textureCoords;
layout(location = 2)in vec3 normal;
**UNIFORM float RenderFlag;** 


void main(void){

if(RenderFlag ==0){
 //Calculate outVariables for normal mapping to the fragment shader
}

if(RenderFlag ==1){
//Calcuate outVariables for parallax mapping to the fragment shader
}

gl_Position = MVPmatrix *vec4(position,1);



}

优步片段着色器:

layout(location = 0) in vec3 position;
layout(location = 1) in vec2 textureCoords;
layout(location = 2)in vec3 normal;
**UNIFORM float RenderFlag;** 
**UNIFORM float reflectionFlag;** // if set either of the 2 render modes               
will have some reflection of the skybox added to it, like reflective   
surface.

void main(void){
if(RenderFlag ==0){
  //display normal mapping


  if(reflectionFlag){
     vec4 reflectColor = texture(cube_texture, ReflectDirR) ;
     //add reflection color to final color and output

  }

}
if(RenderFlag ==1){
//display parrallax mapping
if(reflectionFlag){
    vec4 reflectColor = texture(cube_texture, ReflectDirR) ;

   //add reflection color to final color and output
   }
}
gl_Position = MVPmatrix *vec4(position,1);



}

我的好处是流程简单,但使整个程序更加复杂,我面临着丑陋的嵌套if语句。此外,如果我想完全避免if语句,我需要4个单独的着色器来处理每个可能的分支(没有反射的正常情况:有反射的正常情况:没有反射的视差:有反射的视差),仅针对一个特性——反射。

1:GLSL是否执行两个分支和随后的分支并计算BOTH函数,然后输出正确的函数?

2:是否应该删除反射的if语句,改为无论如何都计算反射颜色,并将其添加到最终颜色中,如果它是相对较小的操作,则使用类似以下内容的统一标志:

finalColor = finalColor + reflectionColor * X 
where X = a uniform variable, if none X == 0, if Reflection X==some amount.

你是如何将uniform加载到着色器程序中的?如果你没有使用缓冲对象,那么你可能无法获得性能优势 - glUniform很耗费资源,多次调用它可能是导致你看到减速的原因。 - BadZen
话虽如此,个人认为(微小的)性能提升几乎肯定不值得编写一个“超级着色器”的额外复杂度。 - BadZen
是的,我正在使用VBO,并且所有的uniforms只有在绘制不同批次的对象时才会更新。 - user4161529
经过一些测试,我决定采用多个着色器。对于我来说,如果分支(嵌套)在着色器中越复杂,则性能下降越多。此外,在链接时添加#include预处理器指令,所有我的着色器都可以共享照明计算等,避免了着色器之间冗余和相同的代码。 - user4161529
我是一个注重利用#define创建着色器变体的粉丝。相关问题请参见这里 - jozxyqk
1个回答

5
首先,让我指出GL4增加了子例程,这是你讨论的两种方法的组合。然而,除非您在一个前向渲染引擎中使用大量单个基本着色器的排列组合,在一帧中多次交换(如果有动态材质系统),否则子例程真的不会提高性能。在我的工作中,我已经花费了一些时间和精力在这方面,并且在一个特定的硬件/驱动程序组合上获得了有价值的改进,在大多数其他情况下没有明显的变化(好或坏)。
我为什么提到子例程?主要是因为你正在讨论微优化,而子例程是为什么在开发的最后阶段才投入大量时间思考的很好的例子。如果你正在努力达到某个性能数字,并且已经从列表中划掉了每个高级优化策略,那么你可以担心这些东西。
话虽如此,几乎不可能回答GLSL如何执行您的着色器。它只是一种高级语言;自从GLSL创建以来,底层硬件架构已经多次改变。最新一代硬件具有实际的分支预测和一些相当复杂的线程引擎,GLSL 1.10级硬件从未拥有过,其中一些直接通过计算着色器暴露出来。
您可以运行数字以查看哪种策略在您的硬件上效果最好,但我认为您会发现这是旧的微优化困境,您甚至可能无法获得足够的可测量性能差异,以猜测采取哪种方法。请记住,“Uber着色器”具有多重优点(并非全部与性能有关),其中最小的一点是,您可以批处理较少且不那么复杂的绘图命令。如果性能没有明显差异,请考虑更简单、更易于实现/维护的设计。

谢谢Andrew,我自己也尝试过子程序,我尝试了多个着色器与统一标志与子程序。我基本上将整个渲染模式包装在一个子程序中,就像它是自己的着色器一样,并且它显着降低了我的帧速率,我基本上将其视为统一标志,但用子程序替换了它。输出是正确的,但我可能实现得不正确。这也是一个不错的阅读材料:http://www.sunandblackcat.com/tipFullView.php?l=eng&topicid=20 - user4161529

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