Search code examples
webgldepth-bufferbatching

Depth Buffer Clear Behavior between Draw Calls?


I have been testing WebGL to see whether I can batch-draw polygons in a particular way. I am going to simplify the use case, but it goes something along the lines of the following:

First, my vertices are simply:

  vertices[v0_xy0, v1_xyz, ... vn_xyz]

In my case, each vertex must have a z value in the range (0 - 100) (I pick 100 arbitrarily) because I want all of those vertices to be depth tested against each other using those z values. On batch N + 1, I am limited to depth values (0 - 100) again, but I need the vertices in this batch to be guaranteed to be drawn atop all previous batches (layers of vertices). In other words, vertices within each batch are depth tested against each, but each batch is just drawn atop the previous one as if there were no depth testing. At first I was going to try drawing to a texture with a framebuffer and depthbuffer attachment, draw to the canvas, repeat for the next group of vertices, but I realized that I might be able to do just this:

// pseudocode
function drawBuffers()
  // clear both the color and the depth
  gl.clearDepth(1.0);
  gl.clear(gl.CLEAR_COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  // iterate over all vertex batches
  for each vertexBatch in vertexBatches do
    // draw the batch with depth testing
    gl.draw(vertexBatch);
    // clear the depth buffer
    /* QUESTION: does this guarantee that subsequent batches 
       will be drawn atop previous batches, or will the pixels be written at  
       random (sometimes underneath, sometimes above)?
    */
    gl.clearDepth(1.0);
    gl.clear(gl.DEPTH_BUFFER_BIT);
  endfor
end drawBuffers

I tested the above by drawing two overlapping quads, clearing the depth buffer, translating left and in negative z (in an attempt to "go under" the previous batch), and drawing the two overlapping quads again. I think that this works because I see that the second pair of quads are drawn in front of the first pair even though their z values are behind the previous pair's z values;

I am not certain that my test is reliable though. Could there be some undefined behavior involved? Is it just a coincidence that my test works as a result of the clearDepth setting and shapes? May I have clarification so I can confirm whether my method will work for sure? Thank you.


Solution

  • Since WebGL is based on OpenGL ES see OpenGL ES 1.1 Full Specification, 4.1.6 Depth Buffer Test, page 104:

    The depth buffer test discards the incoming fragment if a depth comparison fails.

    ....

    The comparison is specified with

    void DepthFunc( enum func );
    

    This command takes a single symbolic constant: one of NEVER, ALWAYS, LESS, LEQUAL, EQUAL, GREATER, GEQUAL, NOTEQUAL. Accordingly, the depth buffer test passes never, always, if the incoming fragment’s zw value is less than, less than or equal to, equal to, greater than, greater than or equal to, or not equal to the depth value stored at the location given by the incoming fragment’s (xw, yw) coordinates.


    This means, if the clear value for the depth buffer glClearDepth is 1.0 (1.0 is the initial value)

    gl.clearDepth(1.0);
    

    and the depth buffer is cleared

    gl.clear(gl.DEPTH_BUFFER_BIT);
    

    and the depth function glDepthFunc is LESS or LEQUAL (LESS is the initial value)

    gl.enable(gl.DEPTH_TEST);
    gl.depthFunc(gl.LEQUAL); 
    

    then the next fragment which is drawn to any (xw, yw) coordinates, will pass the depth test and will overwrite the fragment stored at the location (xw, yw).

    (Of course gl.BLEND has to be disabled and the fragment has to be in clip space)