erken
erken

Reputation: 383

WebGL Float modulus behaviour

I am experiencing strange behaviours for below fragment Shader:

varying vec3 worldSpaceCoords;

uniform float rows;

void main() {

    float testNumber = 7.0;
    vec4 outputColor =  vec4(0.0,0.0,0.0,1.0);

    if(rows == testNumber) {
        if(worldSpaceCoords.x < 0.5) {

            outputColor.x = mod(floor(testNumber * worldSpaceCoords.y), rows ) / rows;

        } else {

            outputColor.x = mod(floor(testNumber * worldSpaceCoords.y), testNumber ) / testNumber;

        }

   } else {

        outputColor  = vec4(1.0,1.0,1.0,1.0);

   }

   gl_FragColor  = outputColor;

}

The "testNumber" variable is just my control variable to check that the passed in uniform "rows" is actually what it should be.

Some numbers of the uniform "rows", gives a strange behaviour. Below is an example where it's set to 9.0 (with testNumber set accordingly).

enter image description here

This result is expected. However, setting rows to 7.0 (and testNumber accordingly), it looks like this:

enter image description here

Clearly something is not working as it should. Any ideas why? I have tried to set the precision to lowp, mediump and highp without any difference.

Thanks

Upvotes: 1

Views: 1262

Answers (1)

rmtoth
rmtoth

Reputation: 46

This is likely the result of imprecise floating-point math, which adheres to rules documented in the GLSL ES Specification. The highp qualifier guarantees that the inputs and outputs of the mod operator are 32-bit IEEE 754 floating point numbers, but the calculation itself is not guaranteed to produce the same high precision. In particular, the modulus operator is defined in Chapter 8.3 as

genType mod (genType x, genType y)

Modulus. Returns x – y * floor (x/y).

The division operator a/b in this expression is guaranteed to have a precision of no less than 2.5 ULP for b (see chapter 4.5.1) and is typically computed as a*(1/b) since reciprocals and multiplications are cheaper than divisions. In your case, 1/testNumber is a compile-time constant and is probably computed to a different (higher) precision than 1/rows, which needs to be computed at run-time.

You could try using integer math instead of floating point, as the integer modulus operator a%b doesn't suffer from such precision issues.

Upvotes: 3

Related Questions