5

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).

Paul Aner
  • 361
  • 1
  • 8

1 Answers1

4

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.

BDL
  • 21,052
  • 22
  • 49
  • 55
  • Thanks, that worked like a charm. I had to change (color.a == 0) to (color.a < 0.7) because the fragment shader seems to interpolate the (small) alpha-array (so at the border there are values between 0 and 1). But then it looks nice (enough). I'm still curious: Why doesn't this work "out of the box" as it does without depth-testing? – Paul Aner Jan 19 '22 at 17:01
  • Full transparent fragments still write to the depth buffer (unless you discard). So if you draw a front circle first, then your depth-buffer contains the full quad of that circle and prevents the next circle behind from being written. – BDL Jan 19 '22 at 17:12
  • But why then did it draw some circles without artifacts (as you can see in the second picture)? Edit: Oh, that's probably just be chance - these circles probably got drawn last, right? – Paul Aner Jan 19 '22 at 17:19
  • Yup. When the circle that's further away get's drawn first, then everything is fine. It's most of the time less costly to discard in the shader compared to sorting all your circles in first place. – BDL Jan 19 '22 at 17:45