在GLSL着色器中使用常量浮点值 - 是否有使用统一变量的理由?

23

我正在查看一个使用着色器的OpenGL应用程序的源代码。其中一个特定的着色器如下所示:

uniform float someConstantValue;
void main()
{
    // Use someConstantValue
}

这个 uniform 只在代码中设置一次,在应用程序运行时不会改变。

在什么情况下,我想将 someConstantValue 声明为 uniform 而不是 const float

编辑: 只是澄清一下,这个常量值是一个物理常数。

3个回答

28

重要原因

错误:循环索引无法与非常量表达式进行比较。

如果我使用

uniform float myfloat;
...
for (float i = 0.0; i < myfloat; i++)

因为myfloat不是一个常量表达式,所以我会得到一个错误。


然而,这个是完全有效的:

const float myfloat = 10.0;
...
for (float i = 0.0; i < myfloat; i++)

为什么?

当GLSL(以及HLSL)被编译成GPU汇编指令时,循环会以非常冗长的方式展开(尽管使用跳转等进行了优化)。这意味着编译时使用myfloat值展开循环;如果该值是一个uniform(即每个渲染调用都可能发生变化),那么该循环直到运行时才能展开(而GPU不做这种即时编译,至少在WebGL中不这样做)。


2
这些情况非常罕见,主要出现在计算着色器中(以及一些骨骼动画系统中)。 - Adrian Seeley
1
现在,你可以期望现代OpenGL和D3D对于uniform值(任何在一个渲染调用期间没有改变的值)进行即时编译。例如,检查布尔类型uniform变量的if语句将在渲染调用启动时被编译掉 - GPU上不会执行运行时检查。更多信息请参见此wiki - Emilian Cioca
3
这是回答问题的相反方面 - OP想知道为什么要使用uniform而不是const,而不是相反的情况。 - Karu
4
在使用OpenGLES/WebGL时,这并不罕见。 - abcthomas

12

首先,使用uniform或constant之间的性能差异可能是可以忽略不计的。其次,仅因为一个值在自然界中始终是常量,并不意味着您在程序中总是希望它保持不变。即使当物理值与现实不符时,程序员也经常会调整这些值以产生最佳效果。例如,在某些类型的游戏中,重力加速度经常增加,以使游戏更快节奏。

如果您不想在代码中设置uniform,可以在GLSL中提供默认值:

uniform float someConstantValue = 12.5;

话虽如此,对于像圆周率这样的变量,使用const是没有问题的,因为它没有被修改的必要。


5
使用统一变量或常量的性能差异可能可以忽略不计。猜测并不好。声明一个常量值允许GLSL编译器执行通常不可能的优化。但是不幸的是,GLSL编译器往往存在漏洞,并表现出不可预测的行为。因此,很难给出一个一般性的答案。我曾经遇到过使用const和非const数组之间的严重性能差异(取决于它们的长度),即使它们的值永远不会被修改。我认为这是编译器的一个bug。 - Tara
3
虽然之前的评论可能在那个时候是准确的,但现在可以预期这种即时编译。统一值可以被优化,就像它们是常量一样。有关更多信息,请参见此链接 - Emilian Cioca
从理论上讲,常量比统一变量更高效吗?但是统一变量在所有顶点着色器调用之间共享,而常量仅在一个调用中初始化/共享。 - XCS

2
我能想到两个原因:
  1. 开发人员在多个应用程序中重复使用着色器库。因此,开发人员尝试保持它们的通用性,而不是为每个应用程序定制每个着色器。

  2. 开发人员预计此变量将成为用户可控制的设置。因此,将其声明为uniform是为即将推出的功能做准备。

如果我是开发人员,并且以上情况都不适用,则我会将其声明为“const”,因为这可以提高性能,而且我不必从我的代码中设置uniform。


1
正如fintelia展示的,您可以在着色器中始终设置uniform的默认(初始)值,这样我就不必从我的代码中设置它了。 - ToolmakerSteve

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