Search code examples
glslshaderintel

smoothstep() returns different values for "identical" arguments


Running a Surface Laptop 3 with Intel® Iris® Plus Graphics (driver version 30.0.101.1191). Perhaps I'm facing a bug in Intel's shader compiler. Though, I have limited experience of shaders in general, so perhaps the behavior below is expected.

Head over to https://www.shadertoy.com/new and try the shader below. For some reason, defining a float d = 1.0 seems to produce different results compared to a compile-time constant of 1.0.

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    float d = 1.0;
    
    // WHY this returns 1?
#if 1
    float x = smoothstep(0.0, 0.0, d);
#else
    // Whereas this returns 0?
    float x = smoothstep(0.0, 0.0, 1.0);
    // OR this for that matter:
    // float x = smoothstep(0.000000000000001, 0.0, d);
#endif
        
    fragColor = vec4(1.0, 0.0, 1.0, 1.0) * x;
}

Thus, smoothstep(0.0, 0.0, 1.0) returns 0 but the equivalent smoothstep(0.0, 0.0, d) returns 1. Why?


Solution

  • smoothstep requires that the first parameter edge0 is strictly less than the second parameter edge1. The results are undefined when edge0 >= edge1.

    The reason behind this is pretty simple. From the GL docs (https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/smoothstep.xhtml)

    The code looks something like this:

    genType t;  /* Or genDType t; */
    t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
    return t * t * (3.0 - 2.0 * t);
    

    Note the denominator,

    (edge1 - edge0)
    

    If those values are equal, there's a divide-by-zero. So, your implementation is returning a 0 or a 1 since it can't throw an exception, because it's C.

    Edit:

    On the question of "why is it returning 0 or 1:" The sample implementation from the GL docs clamps the result from 0 to 1. If edge0 > edge1, the resulting negative value will be clamped to 0. If edge0 == edge1, divide-by-zero results in positive infinity, which is clamped to 1.

    But, that's all speculation, since the actual implmentation in your system's GL implementation is a black box. For cases where edge0 >= edge1, the result is undefined and should be ignored.