在 WebGL 中故意生成 NaN
Making a NaN on purpose in WebGL
我有一个 GLSL 着色器,它应该在满足条件时输出 NaN。我在实际实现时遇到了麻烦。
基本上我想这样做:
float result = condition ? NaN : whatever;
但是 GLSL 似乎没有 NaN 的常量,因此无法编译。 如何生成 NaN?
我尝试自己制作常量:
float NaN = 0.0/0.0; // doesn't work
这适用于我测试过的一台机器,但不适用于另一台机器。它还会在编译着色器时引起警告。
鉴于明显的计算在我试过的其中一台机器上不起作用,我觉得正确地做到这一点是非常棘手的,并且需要了解很多关于各种类型之间不一致的真实世界的事实。 GPU。
欺骗优化器
问题似乎是着色器编译器执行了不正确的优化。基本上,它将 NaN
表达式替换为 0.0
。我不知道 为什么 它会那样做……但它确实如此。也许规范允许未定义的行为?
基于这个假设,我尝试制作一个混淆的方法来生成 NaN
:
float makeNaN(float nonneg) {
return sqrt(-nonneg-1.0);
}
...
float NaN = makeNaN(some_variable_I_know_isnt_negative);
想法是优化器不够聪明,无法看穿这一点。
而且,在失败的测试机器上,这有效!我还尝试将函数简化为 return sqrt(-1.0)
,但这又导致失败(进一步强化了我认为优化器有问题的信念)。
这是一种变通方法,而不是解决方案。
- 一个足够聪明的优化器可以看穿混淆并再次开始破坏。
- 我只在几台机器上测试过,这显然有很大差异。
不要在此处使用 NaN。
OpenGL ES 3.2 Spec 的第 2.3.4.1 节指出
The special values Inf and −Inf encode values with magnitudes too large to be represented; the special value NaN encodes “Not A Number” values resulting from undefined arithmetic operations such as 0/0. Implementations are permitted, but not required, to support Inf's and NaN's in their floating-point computations.
看来真的要靠实现了。您应该输出另一个值而不是 NaN
作为制服传入
与其尝试在 glsl 中生成 NaN,不如在 javascript 中生成,然后传入:
shader = ...
uniform float u_NaN
...
call shader with "u_NaN" set to NaN
Unity glsl 编译器会将 0.0f/0.0f
转换为 intBitsToFloat(int(0xFFC00000u)
- 因为从 OpenGL ES 3.0 开始支持 intBitsToFloat
,这是一个适用于 WebGL2 但不适用于 WebGL1
我有一个 GLSL 着色器,它应该在满足条件时输出 NaN。我在实际实现时遇到了麻烦。
基本上我想这样做:
float result = condition ? NaN : whatever;
但是 GLSL 似乎没有 NaN 的常量,因此无法编译。 如何生成 NaN?
我尝试自己制作常量:
float NaN = 0.0/0.0; // doesn't work
这适用于我测试过的一台机器,但不适用于另一台机器。它还会在编译着色器时引起警告。
鉴于明显的计算在我试过的其中一台机器上不起作用,我觉得正确地做到这一点是非常棘手的,并且需要了解很多关于各种类型之间不一致的真实世界的事实。 GPU。
欺骗优化器
问题似乎是着色器编译器执行了不正确的优化。基本上,它将 NaN
表达式替换为 0.0
。我不知道 为什么 它会那样做……但它确实如此。也许规范允许未定义的行为?
基于这个假设,我尝试制作一个混淆的方法来生成 NaN
:
float makeNaN(float nonneg) {
return sqrt(-nonneg-1.0);
}
...
float NaN = makeNaN(some_variable_I_know_isnt_negative);
想法是优化器不够聪明,无法看穿这一点。
而且,在失败的测试机器上,这有效!我还尝试将函数简化为 return sqrt(-1.0)
,但这又导致失败(进一步强化了我认为优化器有问题的信念)。
这是一种变通方法,而不是解决方案。
- 一个足够聪明的优化器可以看穿混淆并再次开始破坏。
- 我只在几台机器上测试过,这显然有很大差异。
不要在此处使用 NaN。
OpenGL ES 3.2 Spec 的第 2.3.4.1 节指出
The special values Inf and −Inf encode values with magnitudes too large to be represented; the special value NaN encodes “Not A Number” values resulting from undefined arithmetic operations such as 0/0. Implementations are permitted, but not required, to support Inf's and NaN's in their floating-point computations.
看来真的要靠实现了。您应该输出另一个值而不是 NaN
作为制服传入
与其尝试在 glsl 中生成 NaN,不如在 javascript 中生成,然后传入:
shader = ...
uniform float u_NaN
...
call shader with "u_NaN" set to NaN
Unity glsl 编译器会将 0.0f/0.0f
转换为 intBitsToFloat(int(0xFFC00000u)
- 因为从 OpenGL ES 3.0 开始支持 intBitsToFloat
,这是一个适用于 WebGL2 但不适用于 WebGL1