Search code examples
opengl-esdepth-bufferz-orderdepth-testing

OpenGL selected depth testing


I'd like to implement an OpenGL scene (OpenGL ES 2.0) in which depth testing is enabled only between selected triangles.

To be more specific: Suppose we have four triangles to render. Depth testing should be enabled between triangles 1 and 2, as well as between triangles 3 and 4, but neither between triangles 1 and 3 nor triangles 2 and 4.

In other words: Is it possible to enable depth testing within two pairs of triangles but not between them (to improve performance when the rendering sequence of the two pairs can be calculated more easily on the software level?

According to opengl.org FAQ (see bottom of page - 12.080), a scene can be divided into regions for that purpose, but the description doesn't go into enough detail for me to comprehend how that is achieved.


Solution

  • A couple of possibilities come to mind.

    Clear depth buffer multiple times per frame

    The easiest approach is probably to clear the depth buffer after you rendered one group of objects. For the case in the question:

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    // Draw triangle 1.
    // Draw triangle 2.
    glClear(GL_DEPTH_BUFFER_BIT);
    // Draw triangle 3.
    // Draw triangle 4.
    

    Since you won't have depth testing between 1/2 and 3/4, triangles 3 and 4 will be drawn on top of triangles 1 and 2 if they overlap, independent of their relative depth.

    One restriction of this solution is that you have to draw the geometry for each group together. For example, you couldn't use this if for some reason you were rendering the triangles in order 1, 3, 2, 4.

    FBO rendering with multiple depth buffers

    Another option is to use multiple depth buffers. The only way to do this is by using off-screen rendering with FBOs. In this case, you can create multiple depth renderbuffers. Before each draw call, you can then attach the depth buffer you can use to the FBO.

    After you finished rendering the frame to the FBO, you will then have to copy the content to the default framebuffer.

    This has the advantage that you can draw your triangles in any order as long as you switch the depth buffer each time:

    glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
                              GL_RENDERBUFFER, depthBuf1);
    // draw triangle 1
    glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
                              GL_RENDERBUFFER, depthBuf2);
    // draw triangle 3
    glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
                              GL_RENDERBUFFER, depthBuf1);
    // draw triangle 2
    glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
                              GL_RENDERBUFFER, depthBuf2);
    // draw triangle 4
    

    However, switching render buffers can be a fairly expensive operation, so you would still want to minimize how many times you switch the depth buffer.

    So this approach gives you more flexibility, but I would generally expect it to be less efficient. You still need to clear all depth buffers, so you have multiple depth clear operations just like in the first approach. In addition, you have the overhead for switching depth buffers, and for copying the color buffer to the default framebuffer at the end of the frame.