在着色器中除以0会发生什么?

Sté*_*ard 10 iphone shader opengl-es glsl

已知分支在OpenGL ES着色器中特别具有计算成本.在这样的着色器中,我在除以之前检查值是否为null,例如:

if(value == 0.0)
    other_value = 0.0;
else
    other_value = 1.0 / value;
Run Code Online (Sandbox Code Playgroud)

为了加快速度,我想if通过直接做到这一点来避免这种情况:

other_value = 1.0 / value;
Run Code Online (Sandbox Code Playgroud)

我想知道如果value碰巧等于0会发生什么,这在我的治疗中有点罕见,这就是为什么测试它并不容易.着色器是否会崩溃?应用程序崩溃了吗?

bob*_*obo 18

确实未定义.

在iPad模拟器上,将红色分量除以0.0得到0(所有红色都从此图片中消失).

在此输入图像描述

在iPad硬件上,将r分量除以0.0会使其饱和为全​​红色(即+无限制钳位).

在此输入图像描述

所以不,你根本不能依赖未定义的行为来产生一致的结果,但是通过除以0和它本身就没有什么不好的事情发生.

在我的着色器中,我将rgb值除以alpha.碰巧的是,如果alpha值为0,则无论如何都不会显示像素,所以我对分区的任何一个方向都没有问题(给0或+ inf).


Nic*_*ens 5

这样的分支通常并不昂贵。它们通常使用谓词实现。也就是说,GPU计算两个分支,但仅在条件为真的情况下才实际存储指令的结果。因此,不使用跳转指令。汇编代码如下所示:

cmp_eq p0, r0, 0.0   // predicate = (value == 0.0)
(p0) mov r1, 0.0     // other_value = 0.0, if predicate true
(!p0) rcp r1, r0     // other_value = 1.0 / value, if predicate false
Run Code Online (Sandbox Code Playgroud)

请注意,在这种情况下,实际上不必确定第二条指令。无论如何,如其他指出的那样,当分母为零时,除法(倒数)的结果不确定。但是正如您所看到的,您应该能够以很少的廉价指令为代价获得明确的行为(划分通常很慢)。据我所知,所有支持真实分支(跳转指令)的GPU也都支持谓词。着色器编译器将评估是使用谓词还是跳转,并且通常会做正确的事情。

当然,如果您真的不关心除以零的结果,那么可以节省所有的谓词成本。


Les*_*zek 5

代替

float invert_value(in float value)
{
if(value == 0.0)
    return 0.0;
else
    return 1.0 / value;
}
Run Code Online (Sandbox Code Playgroud)

你可以写

float invert_value_ifless(in float value)
{
float sign_value = sign(value);
float sign_value_squared = sign_value*sign_value;
return sign_value_squared / ( value + sign_value_squared - 1.0);
}
Run Code Online (Sandbox Code Playgroud)

这将准确返回您想要的内容

  1. 没有如果
  2. 不能除以 0

不过,我不确定这在最新的硬件上是否真的更快。


eod*_*ash 4

我从未见过着色器因此类事件而崩溃。它很可能会产生垃圾值(如 nan),并扰乱您对结果执行的任何其他计算。除此之外,我不会担心它(并且绝对不会添加分支代码来阻止它)。