openglsdlstencil-bufferocclusion-culling

Occlusion queries with stencil test only


Do occlusion queries still work if I disable depth testing altogether when the obstacle set is known a priori to be strictly in-between the camera and the object to be tested? This is an attempt to improve performance, as, logically, I don't need complex z-tests if none of the occluders are behind the occludee.

I'm using the following commands to initialize color/depth/stencil buffers:

SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 0);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 0);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 0);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1);
...
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

glDisable(GL_CULL_FACE);

glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);

glEnable(GL_STENCIL_TEST);
glStencilMask(0x00000001);
...
glClear(GL_STENCIL_BUFFER_BIT);

Solution

  • The most conclusive document is the latest OpenGL spec. From the OpenGL 4.5 spec, section "17.3.7 Occlusion Queries", on page 476 (with emphasis added by me):

    Occlusion queries use query objects to track the number of fragments or samples that pass the depth test.

    When an occlusion query is active, the samples-passed count is incremented for each fragment that passes the depth test.

    Therefore, the real question becomes: What does "pass the depth test" mean? Does a pixel pass the depth test if there is no depth test? And how does the stencil test come into play?

    The key is that the stencil test is applied before the depth test, which is the behavior defined in the spec. So only fragments that pass the stencil test will go through the depth test, and will therefore be counted in the occlusion query. Or in other words, only fragments that pass both the stencil and depth test are counted.

    One approach that will definitely work is that you enable the depth test, and let all fragments pass the depth test. This will then count all the fragments that passed the stencil test. The settings to use for this are:

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_ALWAYS);
    
    glEnable(GL_STENCIL_TEST);
    ...
    

    Now, will it also work as desired without having a depth buffer, or with the depth buffer disabled? The first part of this is answered at the end of section "17.3.6 Depth Buffer Test":

    If there is no depth buffer, it is as if the depth buffer test always passes.

    In this case, the answer is yes, you can use an occlusion query without a depth buffer, and it will count the fragments that pass the stencil test.

    The second case is covered earlier in section "17.3.6 Depth Buffer Test":

    When disabled, the depth comparison and subsequent possible updates to the depth buffer value are bypassed and the fragment is passed to the next operation.

    Figure 17.1 in the spec shows "Occlusion Query" as the next operation following "Depth Buffer Test". Therefore, all fragments that passed the earlier tests (including stencil) will be counted by the occlusion query if the depth test is disabled.

    And the final answer is: YES, you can use occlusion queries with just a stencil test.

    Acknowledgement: Latest version revised based on feedback by @jozxyqk and @user2464424