WebGL Glitch In Some Intel Integrated Graphics Processors

Viewed 200

I have the situation that a shader produces a completely different output on my Apple (M1) iMac (and also iPhone) than on my Intel (i5-4300U / Haswell-ULT Integrated Graphics Controller) Thinkpad 440 (more info in the comments). Yet there seems to be no difference between browsers, Chrome and Safari resp. Chrome and GNOME Web produce the same image on the iMac resp. laptop. My Intel Thinkpad 13 Chromebook also displays the Intel version (couldn't test other browsers than Chrome there obviously). What could be the reason for this?

[EDIT] Most people (friends and folks who commented below) seem to get the Apple version, so only me and my two laptops apparently get the Intel version?

This is the shader:

  const gl = document.querySelector("canvas").getContext("webgl");
  gl.canvas.width = gl.canvas.height = 512
  gl.viewport(0, 0, 512, 512);
  const programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
  const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
    a_Position: {
      numComponents: 2,
      data: [-1, -1, -1, 3, 3, -1]
    },
  });
  gl.useProgram(programInfo.program);
  twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
  twgl.drawBufferInfo(gl, bufferInfo);
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<script id="vs" type="x-shader/x-vertex">
  attribute vec4 a_Position;

  varying vec2 v_Position;
  varying vec2 v_TexCoord;

  void main() {
      v_Position = a_Position.xy;
      v_TexCoord = a_Position.xy / 2.0 + 0.5;
      gl_Position = a_Position;
  }
</script>
<script id="fs" type="x-shader/x-vertex">
  precision highp float;

  varying vec2 v_Position;
  varying vec2 v_TexCoord;

  vec2 compMult(vec2 u, vec2 v) {
    return vec2(u.x * v.x - u.y * v.y, u.x * v.y + u.y * v.x);
  }

  vec2 G(vec3 x) {
    float lenx = length(x);
    float ang = 350.0 * lenx;
    return vec2(cos(ang), sin(ang)) / lenx;

  }

  void main() {
    gl_FragColor.a = 1.0;
    vec3 x0 = vec3(v_TexCoord, 1);
    vec2 amplitude;
    for(int i = 0; i < 10; i++) {
      for(int j = 0; j < 10; j++) {
        vec2 pos = 0.05 + vec2(i, j) / 10.0;
        vec3 x = vec3(0.4 + 0.2 * pos, 0);
        float disx0x = distance(x0, x);
        amplitude += compMult(
          G(x - vec3(-0.4765625, -0.671875, 300)),
          compMult(
            vec2(-1.0 / disx0x, 350.0),
            G(x - x0) / disx0x
          )
        );
      }
    }
    gl_FragColor.r = length(amplitude) / 10.0;
  }
</script>
<canvas></canvas>

And here are the different outputs:

(Apple)

(Intel)

[EDIT] I think I'm pretty close, look at the following snippet:

  const gl = document.querySelector("canvas").getContext("webgl");
  gl.canvas.width = gl.canvas.height = 512
  gl.viewport(0, 0, 512, 512);
  const programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
  const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
    a_Position: {
      numComponents: 2,
      data: [-1, -1, -1, 3, 3, -1]
    },
  });
  gl.useProgram(programInfo.program);
  twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
  twgl.drawBufferInfo(gl, bufferInfo);
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<script id="vs" type="x-shader/x-vertex">
  attribute vec4 a_Position;

  varying vec2 v_Position;
  varying vec2 v_TexCoord;

  void main() {
      v_Position = a_Position.xy;
      v_TexCoord = a_Position.xy / 2.0 + 0.5;
      gl_Position = a_Position;
  }
</script>
<script id="fs" type="x-shader/x-vertex">
  precision highp float;

  varying vec2 v_Position;
  varying vec2 v_TexCoord;

  vec2 compMult(vec2 u, vec2 v) {
    return vec2(u.x * v.x - u.y * v.y, u.x * v.y + u.y * v.x);
  }

  vec2 G(vec3 x) {
    float lenx = length(x);
    float ang = 350.0 * lenx;
    return vec2(cos(ang), sin(ang)) / lenx;

  }

  void main() {
    gl_FragColor.a = 1.0;
    vec3 x0 = vec3(v_TexCoord, 1);
    vec2 pos = floor(8.0 * v_TexCoord) / 8.0;
    vec3 x = vec3(0.375 + 0.25 * pos, 0);
    float disx0x = distance(x0, x);
    vec2 amplitude = compMult(
      G(x - vec3(-0.46875, -0.5, 300.0)),
      compMult(
        vec2(-1.0 / disx0x, 350.0),
        G(x - x0)
      )
    );
    gl_FragColor.rgb = vec3(0.5 + amplitude, 0.5);
  }
</script>
<canvas></canvas>

This is what it looks like on my Intel machines:

I think, from looking at the algorithm (there is not even a loop anymore), it's pretty clear that the gray areas should not be gray but similar to the other tiles. Also note that I never divide by small numbers, x resp. x0 have z-coordinates of 0 resp. 1, so distance(x, x0) and distance(x, vec3(-0.46875, -0.5, 300)) are never small, which are the only values I use as dividers. Also I changed some constants so that now all numbers have exact binary representations.

1 Answers

It is just a problem with the sin and cos functions on intel systems. The argument seems to exceed the defined value range. The allowed range of values depends on the vendor. This can be solved with the mod function. (4.0*acos(0.0) is 2*PI)

float ang = 350.0 * lenx;

float ang = mod(350.0*lenx, 4.0*acos(0.0));

  const gl = document.querySelector("canvas").getContext("webgl");
  gl.canvas.width = gl.canvas.height = 512
  gl.viewport(0, 0, 512, 512);
  const programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
  const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
    a_Position: {
      numComponents: 2,
      data: [-1, -1, -1, 3, 3, -1]
    },
  });
  gl.useProgram(programInfo.program);
  twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
  twgl.drawBufferInfo(gl, bufferInfo);
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<script id="vs" type="x-shader/x-vertex">
  attribute vec4 a_Position;

  varying vec2 v_Position;
  varying vec2 v_TexCoord;

  void main() {
      v_Position = a_Position.xy;
      v_TexCoord = a_Position.xy / 2.0 + 0.5;
      gl_Position = a_Position;
  }
</script>
<script id="fs" type="x-shader/x-vertex">
  precision highp float;

  varying vec2 v_Position;
  varying vec2 v_TexCoord;

  vec2 compMult(vec2 u, vec2 v) {
    return vec2(u.x * v.x - u.y * v.y, u.x * v.y + u.y * v.x);
  }

  vec2 G(vec3 x) {
    float lenx = length(x);
    float ang = mod(350.0*lenx, 4.0*acos(0.0));
    return vec2(cos(ang), sin(ang)) / lenx;

  }

  void main() {
    gl_FragColor.a = 1.0;
    vec3 x0 = vec3(v_TexCoord, 1);
    vec2 amplitude;
    for(int i = 0; i < 10; i++) {
      for(int j = 0; j < 10; j++) {
        vec2 pos = 0.05 + vec2(i, j) / 10.0;
        vec3 x = vec3(0.4 + 0.2 * pos, 0);
        float disx0x = distance(x0, x);
        amplitude += compMult(
          G(x - vec3(-0.4765625, -0.671875, 300)),
          compMult(
            vec2(-1.0 / disx0x, 350.0),
            G(x - x0) / disx0x
          )
        );
      }
    }
    gl_FragColor.r = length(amplitude) / 10.0;
  }
</script>
<canvas></canvas>

Related