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?..:)