Strange WebGL/GLSL behavior when using zero uniform value and sin/cos

18 views Asked by At

Just getting stuck trying to figure out, what is happening in my WebGL/fragment shader app. Finally I created a minimal example that illustrates the issue.

The code below in my opinion should always produce a red rectangle since sin(0.0000001 + 0.) is close to zero, but not equal to zero.

But on my machine

  • In Chrome I am getting a flickering rectangle (colors are randomly changed between red and black)
  • In Firefox I am always getting ablack rectangle

The code behavior looks strange because the value uniform is being initialized to zero and I do not understand why "small value" + value not equals "small value" + 0.

Full code is here, can be just copy-pasted into an HTML file and launched in a browser:

<html lang="en"><body><script>

const fragmentShader = `#version 300 es
precision highp float;

uniform float value;

out vec4 fragColor;

// void main() {
//   float r = sin(0.0000001 + value);
//
//   fragColor = vec4(r * 10000000., 0, 0, 1);
// }

void main() {
  float t = 0.0000001 + value; // if we replace "value" with "0." or just remove it - we will get red rectangle!
  if (t == 0.0000001) {
    float r = sin(t); // if we replace "t" with "0.0000001" - we get red rectangle!
    fragColor = vec4(r * 10000000., 0, 0, 1);
  } else {
    // notice: we never see blue rectangle!
    fragColor = vec4(0, 0, 1, 1);
  }
}
`;

const vertexShader = `#version 300 es
in vec3 position; void main() { gl_Position = vec4(position, 1); }`;

const canvas = document.createElement('canvas');
document.body.appendChild(canvas);
const gl = canvas.getContext("webgl2");
const program = gl.createProgram();

const compileShader = (type, code) => {
  const shader = gl.createShader(type);
  gl.attachShader(program, shader);
  gl.shaderSource(shader, code);
  gl.compileShader(shader);
};

const bindBuffer = (target, values) => {
  const buffer = gl.createBuffer();
  gl.bindBuffer(target, buffer);
  gl.bufferData(target, values.byteLength, gl.STATIC_DRAW);
  gl.bufferSubData(target, 0, values);
};

bindBuffer(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 0, -1, 1, 0, 1, 1, 0, 1, -1, 0]));
bindBuffer(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 3, 2]));

compileShader(gl.VERTEX_SHADER, vertexShader);
compileShader(gl.FRAGMENT_SHADER, fragmentShader);

gl.linkProgram(program);

const attId = gl.getAttribLocation(program, "position");
gl.enableVertexAttribArray(attId);
gl.vertexAttribPointer(attId, 3, gl.FLOAT, false, 0, 0);

const animationLoop = () => {
  gl.useProgram(program);

  // The problem still there if we remove the line below.
  gl.uniform1f(gl.getUniformLocation(program, "value"), 0);

  gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_SHORT, 0);
};

setInterval(animationLoop, 100);

</script></body></html>

Sorry if this is somewhat a poor explanation, it's quite hard to explain for me what is happening.

I have Windows 11 with NVIDIA RTX 3060, but I tested on other machine as well - the rectangle was black in Chrome.

I've tried to reinstall windows driver, even tried to reinstall Windows - the results were the same. So I suppose that this is not a driver problem.

Does anybody know if I am missing something fundamental in WebGL or this is just some WebGL/driver optimization issue that can be explained in human logic?..:)

0

There are 0 answers