computeFallOff in shadow calculation

Not a bug per see but an undefined behavior according to the spec.

computeFallOff is defined as:

    float computeFallOff(float value, vec2 clipSpace, float frustumEdgeFalloff)
    {
        float mask = smoothstep(1.0 - frustumEdgeFalloff, 1.0, clamp(dot(clipSpace, clipSpace), 0., 1.));
        return mix(value, 1.0, mask);
    }

when frustumEdgeFalloff=0, we get:

   float mask = smoothstep(1.0, 1.0, clamp(dot(clipSpace, clipSpace), 0., 1.));

which is an undefined behavior according to smoothstep - OpenGL 4 Reference Pages :

Results are undefined if edge0 ≥ edge1

where edge0 = edge1 = 1.0 here (with those values, smoothstep returns 0, which seems weird in itself, but makes the mix works as expected).

The obvious workaround is to pass something like frustumEdgeFalloff = 0.000001 and not 0, but it would be better to skip all the calculations and simply returns value. Maybe, in all functions calling computeFallOff, a test should be done against frustumEdgeFalloff and not call computeFallOff when it is 0?

So, instead of:

return computeFallOff(darkness, clipSpace.xy, frustumEdgeFalloff);

do:

return frustumEdgeFalloff == 0 ? darkness : computeFallOff(darkness, clipSpace.xy, frustumEdgeFalloff);

in all shadow related functions.

I don’t know if the conditional test penalty is negated by not calling the function / not doing the falloff computation…

Just clamp the value cpu side :slight_smile: so we do jot have 0 in ???

I don’t understand: which value do we clamp? frustumEdgeFalloff is already 0. Do you mean we set a small value instead of 0?

It should be protected by a define in this case ??? To reduce the shader work if ot needed maybe the define is not set correctly ???

That’s what I wanted to do:

#ifdef SHADOWDISABLEFALLOFF{X}
        #define computeFallOff(value, clipSpace, frustumEdgeFalloff) value
#else
        float computeFallOff(float value, vec2 clipSpace, float frustumEdgeFalloff)
        {
            float mask = smoothstep(1.0 - frustumEdgeFalloff, 1.0, clamp(dot(clipSpace, clipSpace), 0., 1.));
            return mix(value, 1.0, mask);
        }
#endif

The problem being that we are in the shadowsFragmentFunctions.fx file and we can’t use the {X} syntax… We could simply use SHADOWDISABLEFALLOFF, but that would mean all lights used in shading (and that cast shadows) will use the same parameterizing (either SHADOWDISABLEFALLOFF is defined or not, for all lights).

For now, I guess you could simply put:

return mix(value, 1.0000000001, mask);

What we need is return a struct in the compute shadows and use computFallOff in the caller to #define it in the long run.

We could do it right after CSM to not do too many changes at once.

The undefined thing is with smoothstep, however.

Agree.

LoL :slight_smile:

clamp(smoothstep(1.0 - frustumEdgeFalloff, 1.00000000001, dot(clipSpace, clipSpace)), 0., 1.);

And we ll fix the rest right after