From the title, I have a shader where my a floating point number is apparently not NaN, is apparently not Ininity, and is apparently not equal to, greater than, or lesser than 0. What?
The function that generates this floating point number is this:
float ray1(vec3 rayO, vec3 rayD){
float a = rayD.x;
float b = rayD.y;
float c = rayD.z;
float x0 = rayO.x;
float y0 = rayO.y;
float z0 = rayO.z;
float alpha = 0.1 * ((a * a * c) + (b * b * c));
float beta = 0.1 * ((a * a * z0) + (2. * x0 * a * c) + (b * b * z0) + (2. * y0 * b * c));
float gamma = 0.1 * ((2. * x0 * z0 * a) + (x0 * x0 * c) + (2. * y0 * z0 * b) + (y0 * y0 *c)) + (0.6 * c);
float rho = 0.1 * ((z0 * x0 * x0) + (z0 * y0 * y0)) + (0.6 * z0) + 1.;
float P = -beta / (3. * alpha);
float Q = (P * P * P) + (((beta * gamma) - (3. * alpha * rho)) / (6. * (alpha * alpha)));
float R = gamma / (3. * alpha);
float M = (Q * Q) + pow(pow(R - (P * P), 3.), .5);
float t = pow(Q + M, .333) + pow(Q - M, .333) + P;
return t;
}
I am passing this to it:
float t0 = ray1(vec3(0.), vec3(1.));
When I check what t0
is with any of these ifs (one at a time):
if(isnan(t0)){
col = vec4(.5);
}
if(t0 <= 0.){
col = vec4(.5);
}
if(t0 >= 0.){
col = vec4(.5);
}
if(isinf(t0)){
col = vec4(.5);
}
they all return false.
What is t0
? Why is this happening?
Update: I'm fairly certain that the issue lies with taking the cube root of Q, because when I do return pow(Q, .333);
instead, it gives the same result.
This appears to be the result of some aggressive optimization by the compiler. With some algebra, you can determine that as of this line:
float t = pow(Q + M, .333) + pow(Q - M, .333) + P;
the value of Q
is -2.5 and M
is 7.25. Thus, the second pow
will produce a NaN, as will the whole expression, and as far as the compiler is concerned the entire ray1
function call can be optimized away.
The fact that the compiler then seems to optimize away the NaN-checking if
statement as well could be seen as a bug, perhaps, but it's relevant to note that implementations of WebGL aren't required to support NaN in the first place. (See user ibesora's answer on this NaN-related question.) In other words, having your shader's behavior depend on a value that's guaranteed to be NaN at compile time leads to undefined behavior.
To confirm that this is optimization-related, I modified the line that sets M
as follows:
float M = (Q * Q) + pow(pow(R - (P * P), 3.), .5) + (iDate.x - 2022.);
With this change, the image becomes gray as you might be expecting.