目前我正在学习如何在GLSL中为我正在制作的游戏引擎创建着色器,我对这个令我困惑的语言有疑问。我已经了解到,在低于3.0的着色器版本中,您不能在循环条件下使用统一变量。例如,以下代码不适用于早于3.0的着色器版本。
for (int i = 0; i < uNumLights; i++)
{
...............
}
但是不可能用一个具有固定迭代量的循环来替换它,但是如果i(在这种情况下,i)大于uNumLights,则包含一个条件语句会破坏循环。例如:
for (int i = 0; i < MAX_LIGHTS; i++)
{
if(i >= uNumLights)
break;
..............
}
这些不等同吗?后者应该使用旧版本的GLSL吗?如果是这样,这比我读过的其他技术更有效,更容易实现,比如使用不同版本的着色器来获得不同数量的灯光?
我知道这可能是一个愚蠢的问题,但我是一个初学者,我找不到为什么这不起作用的原因。
答案 0 :(得分:12)
GLSL可能令人困惑,因为for()
建议您必须有条件分支,即使没有条件分支,因为硬件根本无法完成(适用于if()
in同样的方式)。
在SM3之前的硬件上发生的真正的是,你的OpenGL实现中的HAL将完全展开你的循环,所以实际上没有跳转了。并且,这解释了为什么使用非常数很难做到这一点。
虽然技术上可以使用非常量来实现,但每次更改该制服时,实现都必须重新编译着色器,如果您只是允许提供任何随机数,它可能会违反最大指令数
这是一个问题因为......然后呢?那是一个糟糕的情况。
如果提供的常量太大,则在构建着色器时会给出“太多指令”编译器错误。现在,如果你在制服中提供一个愚蠢的数字,并且HAL因此必须产生新的代码并且在这个限制下运行,那么OpenGL可以做什么呢?
您很可能在编译和链接后验证了您的程序,并且您最有可能查询着色器信息日志,并且OpenGL一直告诉您一切都很好。在某种程度上,这是一个有约束力的承诺,它不能一下子就决定。因此,它必须确保不会出现这种情况,唯一可行的解决方案是不允许在不支持动态分支的硬件代的情况下使用制服。
否则,glUniform
内部需要进行某种形式的验证,以拒绝不良值。但是,由于这取决于成功(或不成功)着色器重新编译,这意味着它必须同步运行,这使得它成为一种“不行”的方法。另外,请考虑GL_ARB_uniform_buffer_object
在某些SM2硬件(例如GeForce FX)上暴露,这意味着您可以在OpenGL中抛出具有不可预测内容的缓冲区对象,并且仍然期望它以某种方式工作!在取消映射后,实现必须扫描缓冲区的内存中的无效值,这是疯了。
与循环类似,if()
语句不会在SM2硬件上分支,即使它看起来像。相反,它将计算两个分支并进行有条件的移动。
答案 1 :(得分:2)
(我假设你在谈论像素着色器) 第二个变体仅适用于支持着色器模型&gt; = 3的gpu。因为gpu着色器模型&lt;不支持动态分支(例如将变量 uNumLights 置于IF条件中)。 3要么。
Here您可以比较不同着色器模型之间和不支持的内容。
答案 2 :(得分:0)
我刚刚发现了一个有趣的工作。看起来很愚蠢,我不能向您保证这是一个健康的选择,但它现在似乎对我有用:
将您的 for 循环设置为您允许的最大值。如果计数超出您的统一值,则在循环中放置一个条件以跳过繁重的例程。
uniform int iterations;
for(int i=0; i<10; i++){
if(i<iterations){
//do your thing...
}
}