我见过很多像这样的 GLSL 代码:
vec2 x = y * 0.00390625;
Run Code Online (Sandbox Code Playgroud)
通常,为了清楚起见,我将其写为:
vec2 x = y * (1.0 / 256.0);
Run Code Online (Sandbox Code Playgroud)
或作为:
vec2 x = y / 256.0;
Run Code Online (Sandbox Code Playgroud)
使用顶级版本是否合理?在我看来,如果您的编译器不支持数字文字的常量折叠或简单的强度降低,那么它确实必须非常可悲。
所以我的问题可以改写:您是否可能真的遇到不支持常量折叠或强度降低的 GLSL 编译器?我只是在考虑 OpenGL 实现中的 GLSL 编译器,而不是 GLSL 优化器。
我不是编译器专家,但如果 GLSL 编译器不进行简单的常量折叠,我会感到非常惊讶。因此,我认为可以安全地假设以下情况下常量的除法在编译时得到评估:
vec2 x = y * (1.0 / 256.0);
Run Code Online (Sandbox Code Playgroud)
第二种情况实际上更有趣:
vec2 x = y / 256.0;
Run Code Online (Sandbox Code Playgroud)
如果这是 C 或 C++ 代码,我的理解是编译器默认情况下不会用乘法替换它。原因是由于舍入不同,无法保证结果与最后一位相同。作为参考,请参阅优化浮点除法和转换操作中的答案,其中一个答案确认例如 gcc 不会进行替换,除非使用-freciprocal-math编译器选项。
但是,GLSL 是另一种情况。OpenGL ES GLSL 规范特别允许这种类型的转换:
C++ 标准要求表达式必须按照操作的优先级指定的顺序进行计算,并且只有在结果相同或结果未定义的情况下才能重新组合。不得应用影响操作结果的其他变换。GLSL ES 通过以下方式放宽了这些要求:
...
浮点除法可以用倒数和乘法代替。
有趣的是,这不是完整(即非 ES)GLSL 规范的一部分。它确实有精度要求,即除法的误差最多为 2.5 ULP(最后一个单位)。我对此的解释方式是,如果用乘法替换除法满足该精度要求,则替换将是合法的。但是为了确保您的代码尽可能高效,在可能的情况下使用乘法而不是除法似乎仍然更安全。