在OpenGL着色器中检测NaN的最佳方法

20

今天早上遇到了一个看似神秘的错误,我很幸运地很快就找到了解决办法。

我在片段着色器中通过除以计数器产生一个平均值,当计数器为零时,结果颜色值变为 NaN。

Nvidia在混合时优雅地将 NaN 视为 0 值,但是 Intel 不会,并且似乎会级联 NaN,导致片段变成黑色。

所以这个错误一直存在,直到我在 Intel 机器上测试代码时才发现。

我想知道是否有什么方法可以“捕获”无效值。看起来,就像普通编程一样,处理这种情况的唯一可靠方法(即使这样也不完全可靠)是仔细考虑除数所有可能的情况。

检测 NaN 的标准方法是查看数字是否与自身不相等。我能否构建一个调试着色器,检查每个片段是否与自身不相等,如果满足条件,设置一个闪烁、醒目的颜色?GLSL 是否允许我以这种方式检测 NaN,还是当值无效时我只能使用未定义的行为?


你为什么要除以0呢?难道你不能捕获计数器为0或更改算法吗? - djmj
3
作为程序员,始终检查潜在的分母是否可能为零是合理的,但检查可能会导致性能成本。我的问题与事后检测坏值有关。 - Steven Lu
2个回答

28

我在片段着色器中使用一个计数器除以一个数字来产生平均值,当计数器为零时,结果的颜色值变成了NaN。

浮点数并不是这样工作的。当你除以零时,你会得到INF,而不是NaN。除非分子也是零,否则你会得到NaN。

无论如何,GLSL提供了isinfisnan函数,它们可以实现所述功能。


2
啊,那非常方便。原来它们是NaN,因为如果分母为零,分子总是为零。 - Steven Lu

11

缺乏isnan对WebGL和Opengl ES 2.0构成问题。我有机会尝试过适用于所有GPU的Polyfill:

bool isnan( float val )
{
  return ( val < 0.0 || 0.0 < val || val == 0.0 ) ? false : true;
  // important: some nVidias failed to cope with version below.
  // Probably wrong optimization.
  /*return ( val <= 0.0 || 0.0 <= val ) ? false : true;*/
}

2
OpenGL ES GLSL 3+具有isnan函数。 - Colonel Thirty Two
1
由于NVIDIA的优化器非常激进,因此无法处理这个问题。 - Ruslan
2
Java将其定义为boolean isNaN(float f) {return f != f;},FWIW。 - barneypitt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接