2011-02-05 85 views
6

目前我正在学习如何在GLSL中为我正在开发的游戏引擎创建着色器,并且我对于困惑我的语言有疑问。我已经了解到,在着色器版本低于3.0的情况下,你不能在循环条件下使用统一变量。例如,以下代码在3.0以前的着色器版本中不起作用。GLSL break命令

for (int i = 0; i < uNumLights; i++) 
{ 
    ............... 
} 

但是是不是可以用迭代的固定量与循环替换此,而是包含会打破循环,如果我,在这种情况下,比uNumLights大的条件语句?例如:

for (int i = 0; i < MAX_LIGHTS; i++) 
{ 
    if(i >= uNumLights) 
     break; 
    .............. 
} 

这些不等于吗?后者是否应该用旧版本的GLSL?如果是这样,这不是比我已经阅读过的其他技术更高效,更容易实现,比如对不同数量的灯使用不同版本的着色器。
我知道这可能是一个愚蠢的问题,但我是一个初学者,我找不到一个原因,为什么这不应该工作。

回答

11

GLSL可能会引起混淆,因为for()建议您必须有条件分支,即使没有,因为硬件根本无法完成(这适用于if())。

什么真的发生在SM3以前的硬件上是因为OpenGL实现中的HAL将完全展开您的循环,因此实际上不再有跳转。而这就解释了为什么用非常量来解决这个问题有困难。

虽然在技术上可以用非常量来完成它,但是实现必须在每次更改该制服时重新编译着色器,并且如果您只允许提供任何偶然性,它可能会针对最大指令计数运行数。

这是一个问题,因为...然后呢?这是一个糟糕的情况。

如果您提供的常量太大,则在构建着色器时会给您提供“太多指令”编译器错误。现在,如果你在制服中提供一个愚蠢的数字,并且HAL因此必须产生新的代码并且违背这个限制,那么OpenGL可以做什么?
你最有可能在编译和链接之后验证你的程序,并且你很可能会查询着色器信息日志,并且OpenGL一直告诉你一切正常。这在某种程度上是一个有约束力的承诺,它不能只是突然决定。因此,它必须确保不会出现这种情况,唯一可行的解​​决方案是不允许在不支持动态分支的硬件世代中使用制服。
否则,需要在glUniform内部进行某种形式的验证,以拒绝不良值。但是,由于这取决于成功(或不成功)的着色器重新编译,这意味着它必须同步运行,这使得它成为“不可行”的方法。此外,请考虑GL_ARB_uniform_buffer_object在某些SM2硬件(例如GeForce FX)上公开,这意味着您可以在OpenGL中引发具有不可预知内容的缓冲区对象,并仍然期望它以某种方式工作!在你取消映射之后,这个实现将不得不扫描缓冲区的内存中的无效值,这是疯狂的。

与循环类似,if()语句不会在SM2硬件上分支,即使看起来像这样。相反,它会计算两个分支并进行有条件的移动。

2

(我假设你正在谈论像素着色器)。
第二个变体只在支持着色器模型> = 3的GPU上工作。因为在GPU着色器模型< 3上也不支持动态分支(例如将变量uNumLights置入IF条件)。

Here您可以比较不同着色器模型之间和不支持的内容。

+0

目前我正在写一个旧显卡的着色器,它只支持着色器模型2.0。奇怪的是,第二个变体确实起作用,或者至少编译没有错误。至于功能,我不确定它是否在停止命令被触发时停止。第一个当然不会编译。 – 2011-02-05 18:37:42