为什么人们在GLSL中写入0.00390625而不是1.0 / 256.0?

时间:2016-01-27 03:04:40

标签: opengl glsl

我看过很多看起来像这样的GLSL代码:

vec2 x = y * 0.00390625;

通常,为了清楚起见,我将其写为:

vec2 x = y * (1.0 / 256.0);

或者作为:

vec2 x = y / 256.0;

使用顶级版本是否合理?在我看来,如果它不支持数字文字的常量折叠或简单的强度降低,那么你的编译器必须非常可怜。

所以我的问题可以改写一下:你是否真的遇到GLSL编译器而不支持恒定折叠或强度降低?我只是考虑OpenGL实现中的GLSL编译器,而不是GLSL优化器。

1 个答案:

答案 0 :(得分:4)

我不是编译专家,但如果GLSL编译器没有进行简单的常量折叠,我会非常惊讶。所以我认为应该可以安全地假设在以下情况下常量的除法在编译时得到评估:

vec2 x = y * (1.0 / 256.0);

第二种情况实际上更有趣:

vec2 x = y / 256.0;

如果这是C或C ++代码,我的理解是编译器默认情况下通过乘法替换它。原因是由于不同的舍入,无法保证结果与最后一位相同。作为参考,请参阅Optimizing a floating point division and conversion operation中的答案,其中一个答案确认例如gcc不进行替换,除非使用-freciprocal-math编译器选项。

然而,GLSL是一个不同的案例。 OpenGL ES GLSL规范特别允许这种类型的转换:

  

C ++标准要求表达式必须按照操作优先级指定的顺序进行计算,并且只有在结果相同或结果未定义的情况下才能重新分组。不会应用任何影响操作结果的其他变换。 GLSL ES通过以下方式放宽了这些要求:

     

...

     

浮点除法可以用倒数和乘法代替。

有趣的是,这是完整(即非ES)GLSL规范的一部分。它确实有精度要求,即除法误差最多为2.5 ULP(最后一个单位)。我解释这一点的方式,如果用乘法替换除法满足精度要求,则替换将是合法的。但是为了确保您的代码尽可能高效,在可能的情况下使用乘法而不是分区似乎更安全。