javascriptwebgldepth-bufferzbuffer

Is there a bug in WebGL polygonOffset or am I missing something?


I am seeing very odd behavior where polygonOffset initially works, but if I re-render it stops working.

I made a simple example to illustrate it. I started with the z-fighting example from Ch7 of the WebGL Programming Guide (https://sites.google.com/site/webglbook/). I then separated out just the rendering portion and wrapped it in a function. I then hooked up an HTML button to call the render() function when clicked. On the first click, the triangles render correctly with no issues. On the second click, it is like polygonOffset is turned off again.

I've tried a number of different variations, including re-enabling every time, disabling and re-enabling, changing the offsets, but I keep getting the same behavior. Any ideas?

I'm including the code, though the snippet doesn't run for me won't run without the book's libraries.

// Zfighting.js (c) 2012 matsuda
// Vertex shader program
var VSHADER_SOURCE =
  'attribute vec4 a_Position;\n' +
  'attribute vec4 a_Color;\n' +
  'uniform mat4 u_ViewProjMatrix;\n' +
  'varying vec4 v_Color;\n' +
  'void main() {\n' +
  '  gl_Position = u_ViewProjMatrix * a_Position;\n' +
  '  v_Color = a_Color;\n' +
  '}\n';

// Fragment shader program
var FSHADER_SOURCE =
  '#ifdef GL_ES\n' +
  'precision mediump float;\n' +
  '#endif\n' +
  'varying vec4 v_Color;\n' +
  'void main() {\n' +
  '  gl_FragColor = v_Color;\n' +
  '}\n';

function main() {
  // Retrieve <canvas> element
  var canvas = document.getElementById('webgl');

  // Get the rendering context for WebGL
  var gl = getWebGLContext(canvas);
  if (!gl) {
    console.log('Failed to get the rendering context for WebGL');
    return;
  }

  // Initialize shaders
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    console.log('Failed to intialize shaders.');
    return;
  }

  // Set the vertex coordinates and color (the blue triangle is in the front)
  var n = initVertexBuffers(gl);
  if (n < 0) {
    console.log('Failed to set the vertex information');
    return;
  }

   //Set clear color and enable the hidden surface removal function
  gl.clearColor(0, 0, 0, 1);
  gl.enable(gl.DEPTH_TEST);

  // Get the storage locations of u_ViewProjMatrix
  var u_ViewProjMatrix = gl.getUniformLocation(gl.program, 'u_ViewProjMatrix');
  if (!u_ViewProjMatrix) { 
    console.log('Failed to get the storage locations of u_ViewProjMatrix');
    return;
  }

  var viewProjMatrix = new Matrix4();
  // Set the eye point, look-at point, and up vector.
  viewProjMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);
  viewProjMatrix.lookAt(3.06, 2.5, 10.0, 0, 0, -2, 0, 1, 0);

  // Pass the view projection matrix to u_ViewProjMatrix
  gl.uniformMatrix4fv(u_ViewProjMatrix, false, viewProjMatrix.elements);

  // Enable the polygon offset function
  gl.enable(gl.POLYGON_OFFSET_FILL);
  
  function render() {
  // Clear color and depth buffer
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

  
  // Draw the triangles
  gl.drawArrays(gl.TRIANGLES, 0, n/2);   // The green triangle
    gl.polygonOffset(1.0, 1.0);          // Set the polygon offset
  gl.drawArrays(gl.TRIANGLES, n/2, n/2); // The yellow triangle
}
 
 document.getElementById("button").onclick = render;
}

function initVertexBuffers(gl) {
  var verticesColors = new Float32Array([
    // Vertex coordinates and color
     0.0,  2.5,  -5.0,  0.4,  1.0,  0.4, // The green triangle
    -2.5, -2.5,  -5.0,  0.4,  1.0,  0.4,
     2.5, -2.5,  -5.0,  1.0,  0.4,  0.4, 

     0.0,  3.0,  -5.0,  1.0,  0.4,  0.4, // The yellow triagle
    -3.0, -3.0,  -5.0,  1.0,  1.0,  0.4,
     3.0, -3.0,  -5.0,  1.0,  1.0,  0.4, 
  ]);
  var n = 6;

  // Create a buffer object
  var vertexColorbuffer = gl.createBuffer();  
  if (!vertexColorbuffer) {
    console.log('Failed to create the buffer object');
    return -1;
  }

  // Write the vertex coordinates and color to the buffer object
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorbuffer);
  gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);

  var FSIZE = verticesColors.BYTES_PER_ELEMENT;
  // Assign the buffer object to a_Position and enable the assignment
  var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
  if(a_Position < 0) {
    console.log('Failed to get the storage location of a_Position');
    return -1;
  }
  gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE * 6, 0);
  gl.enableVertexAttribArray(a_Position);
  // Assign the buffer object to a_Color and enable the assignment
  var a_Color = gl.getAttribLocation(gl.program, 'a_Color');
  if(a_Color < 0) {
    console.log('Failed to get the storage location of a_Color');
    return -1;
  }
  gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3);
  gl.enableVertexAttribArray(a_Color);

  return n;
}
<canvas id="webgl" width="400" height="400">
    Please use a browser that supports "canvas"
    </canvas>
<input type="button" id="button" />


Solution

  • You need to reset PolygonOffset or disable/reenable it, otherwise both triangles are offset by the same amount.

    GPUs are state machines, you're in charge of managing the state(variables):

    function render() {
      // Clear color and depth buffer
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    
      // Draw the triangles
      gl.polygonOffset(0.0, 0.0);          // Reset the polygon offset
      gl.drawArrays(gl.TRIANGLES, 0, n/2);   // The green triangle
      gl.polygonOffset(1.0, 1.0);          // Set the polygon offset
      gl.drawArrays(gl.TRIANGLES, n/2, n/2); // The yellow triangle
    }