In GLSL (specifically 3.00 that I\'m using), there are two versions of
atan()
: atan(y_over_x)
can only return angles between -PI/2, PI/2, while
I'm going to answer my own question to share my knowledge. We first notice that the instability happens when x
is near zero. However, we can also translate that as abs(x) << abs(y)
. So first we divide the plane (assuming we are on a unit circle) into two regions: one where |x| <= |y|
and another where |x| > |y|
, as shown below:
We know that atan(x,y)
is much more stable in the green region -- when x is close to zero we simply have something close to atan(0.0) which is very stable numerically, while the usual atan(y,x)
is more stable in the orange region. You can also convince yourself that this relationship:
atan(x,y) = PI/2 - atan(y,x)
holds for all non-origin (x,y), where it is undefined, and we are talking about atan(y,x)
that is able to return angle value in the entire range of -PI,PI, not atan(y_over_x)
which only returns angle between -PI/2, PI/2. Therefore, our robust atan2()
routine for GLSL is quite simple:
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);
}
As a side note, the identity for mathematical function atan(x)
is actually:
atan(x) + atan(1/x) = sgn(x) * PI/2
which is true because its range is (-PI/2, PI/2).