11
$\begingroup$

I have an OpenGL application which uses stencil tests quite extensively to render irregular shapes (a bit like a simple 2-D CSG). If I could find out how many fragments passed the stencil test and were actually rendered, this would be very helpful in simplifying some computations down the line. Specifically, it would allow me to determine the area of the rendered shape for free instead of having to approximate it with a Monte Carlo simulation later on.

I know that there is a similar concept for primitives emitted from the geometry shader, called transform feedback. I'd like to know if a similar concept exists for fragments and the stencil test.

$\endgroup$
  • $\begingroup$ A crude solution would be to just paint one contrasting color onto another through the stencil, save that buffer out, and count the number of pixels that were altered. $\endgroup$ – TheBuzzSaw Aug 4 '15 at 18:28
  • $\begingroup$ Hmm, the specification says occulsion queries count the number of fragments that pass the depth test, but off the top of my head I'm not sure how that interacts with the stencil test right now. $\endgroup$ – Christian Rau Aug 4 '15 at 18:33
  • $\begingroup$ @ChristianRau It seems that only the fragments that pass the depth tests will be counted, but stencil, discard and alpha tests are ignored. $\endgroup$ – Maurice Laveaux Aug 4 '15 at 22:54
  • 2
    $\begingroup$ @ChristianRau and Maurice, the original ARB_occlusion_query spec explicitly says it counts samples passing both depth and stencil tests, though. See also this StackOverflow question. $\endgroup$ – Nathan Reed Aug 5 '15 at 6:19
  • $\begingroup$ @NathanReed Sounds like you're about to write an answer. $\endgroup$ – Christian Rau Aug 5 '15 at 14:00
10
$\begingroup$

One possible approach could be the use of Hardware Occlusion Query.

You can use the facts that, by specification, the Stencil Test is executed before the depth test, and only the fragments that pass the depth test are counted by the Occlusion Query.

A simple example (not tested) would be like:

    GLuint samples_query = 0;
    GLuint samples_passed = 0;
    glGenQueries(1, &samples_query);
    // Initialize your buffers and textures ...
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_STENCIL_TEST);

    // Set up the values on the stencil buffer ...

    // Now we count the fragments that pass the stencil test
    glDepthFunc(GL_ALWAYS); // Set up the depth test to always pass
    glBeginQuery(GL_SAMPLES_PASSED, samples_query);
    // Render your meshes here
    glEndQuery(GL_SAMPLES_PASSED);
    glGetQueryObjectuiv(samples_query, GL_QUERY_RESULT, &samples_passed);
    // samples_passed holds the number of fragments that passed the stencil test (if any)

    // Release your resources ...
    glDeleteQueries(1, &samples_query);

Note that the call to obtain the number of samples will call forcibly the flush of the pipeline and wait for the query to finish. If you need a more asynchronous approach you can query wether the occlusion query is done or not by using:

    GLuint query_done = 0;
    glGetQueryObjectuiv(samples_query, GL_QUERY_RESULT_AVAILABLE, &query_done);
    if (query_done != 0)
        // Your query result is ready
    else
        // Maybe check the next frame?
$\endgroup$
2
$\begingroup$

If what you are interested in is the area, you could downsize the stencil buffer until you reach one pixel and deduce that area from its color.

Steps would be:

  • Copy the stencil to a texture, using a format with enough precision.
  • Load a shader that outputs a color proportional to the number of texels with a given color.
  • Ping-pong between to framebuffers to reduce the size by half until reaching one pixel.
  • The color of the pixel is the percentage of the viewport covered by the area: just multiply it by the area of the viewport.
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.