在GLSL(特别是我正在使用的3.00)中,有两个版本的atan():atan(y_over_x)只能返回-PI/2,PI/2之间的角度,而atan(y/x)可以考虑所有4个象限,因此角度范围涵盖了-PI,PI的所有内容,就像C++中的atan2()一样。

我想使用第二个atan将XY坐标转换为角度。
但是,GLSL中的atan()除了无法处理x = 0之外,还不是很稳定。尤其是在x接近于零的情况下,除法运算可能会溢出,从而导致相反的结果角度(您得到的值近似于-PI/2,而您假设该值近似为PI/2)。

我们可以在GLSL atan(y,x)的基础上构建一个更好的简单实现,以使其更强大吗?

最佳答案

我将回答自己的问题,以分享我的知识。我们首先注意到x接近零时会发生不稳定性。但是,我们也可以将其翻译为abs(x) << abs(y)。因此,首先我们将平面(假设我们在单位圆上)划分为两个区域:一个区域为|x| <= |y|,另一个区域为|x| > |y|,如下所示:

我们知道atan(x,y)在绿色区域中更加稳定-当x接近零时,我们仅拥有接近atan(0.0)的值(在数值上非常稳定),而通常的atan(y,x)在橙色区域更稳定。您还可以使自己相信这种关系:

atan(x,y) = PI/2 - atan(y,x)

对于所有未定义的非原点(x,y)都成立,我们正在谈论的atan(y,x)可以返回整个-PI,PI范围内的角度值,而不是atan(y_over_x)只能返回-PI/2,PI/2。因此,用于GLSL的健壮的atan2()例程非常简单:
float atan2(in float y, in float x)
{
    bool s = (abs(x) > abs(y));
    return mix(PI/2.0 - atan(x,y), atan(y,x), s);
}

附带说明一下,数学函数atan(x)的标识实际上是:
atan(x) + atan(1/x) = sgn(x) * PI/2

这是正确的,因为其范围是(-PI/2,PI/2)。

10-08 00:34