Search code examples
shaderglsles

Is there GLSL problem with float uniform rounding


I pass Unix time to my shader from Java code: gl.uniform1f(shader, System.currentTimeMillis());

The shader looks like (simple form, just to investigate the problem):

#ifdef GLES
precision highp float;
#endif

uniform float u_time;

void main() {
  int d = 1000;
  float result = floor(u_time / d) * d;
  if (result > u_time) {
    s = 1.0;
  } else {
    s = 0.5;
  }
  gl_FragColor = vec4(s, 0, 0, 1.0);
}

I expect that result will never be greater than u_time. Surprisingly it IS (shader produces bright red color).

Moreover, if replace value of d with 100, for example, it works as expected - renders dark red.

Tried to replace d with float value of 100.0, but without any effect.

It works even if I replace u_time in expression with direct float constant, for example 1563854457241.0 - it is about the same, that Java puts to shader.

What may be the problem? I suspect it relates to precision, but do not undertand how to fix it.

P.S. After all, I got TRUE (bright red color) if replace condition expression with

1000000123.0 * 1000 == 1000000123456.0

Nuf said.


Solution

  • In your shader code you've exceeded the precision limit of a 32 bit floating point number.
    See IEEE 754 and GLSL ES -Precision and Precision Qualifiers.
    When you use the precision qualifier highp, then the number of significant bit is 23 (+1 for the sign), so a number like 1000000123456.0 can not be represented with the full precision, because the approximated number of decimal digits is 7.22.