Search code examples
c++openglglslblendingdepth-testing

OpenGL depth testing and blending not working simultaniously


I'm currently writing a gravity-simulation and I have a small problem displaying the particles with OpenGL.

To get "round" particles, I create a small float-array like this:

for (int n = 0; n < 16; n++)
            for (int m = 0; m < 16; m++)
            {
                AlphaData[n * 16 + m] = ((n - 8) * (n - 8) + (m - 8) * (m - 8) < 64);
            }

I then put this in a GL_TEXTURE_2D with format GL_RED. In the fragment shader (via glDrawArraysInstanced), I draw the particles like this:

color = vec4(ParticleColor.rgb, texture(Sampler, UV).r);

This works as it should, producing a picture like this (particles enlarged for demonstration):

enter image description here

As you can see, no artifacts. Every particle here is the same size, so every smaller one you see on a "larger" particle is in the background and should not be visible. When I turn on depth-testing with

glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);

I get something like this: enter image description here

So for the most part, this looks correct ("smaller" particles being behind the "bigger" ones). But I now have artifacts from the underlying quads. Weirdly not ALL particles have this behavior.

Can anybody tell me, what I'm doing wrong? Or do depth-testing and blending not work nicely together?

I'm not sure, what other code you might need for a diagnosis (everything else seems to work correctly), so just tell me, if you need additional code.

I'm using a perspective projection here (of course for particles in 3D-space).


Solution

  • You're in a special case where your fragments are either fully opaque or fully transparent, so it's possible to get depth-testing and blending to work at the same time. The actual problem is, that for depth testing even a fully transparent fragment will store it's depth value. You can prevent the writing by explicitly discarding the fragment in the shader. Something like:

    color = vec4(ParticleColor.rgb, texture(Sampler, UV).r);
    if (color.a == 0.0)
        discard;
    

    Note, that conditional branching might introduce some additional overhead, but I wouldn't expect too many problems in your case.

    For the general case with semi-transparent fragments, blending and depth-testing at the same time will not work. In order for blending to produce the correct result, you have to depth sort your geometry prior to rendering and render from back to front.