0

I am trying to render textured particles and i have the problem. Transparent pixels of texture doing a weird thing with render. Looks like particles that are behing nearest (to camera) particles are not rendering at all. But not always, some of them are rendering and look expected. I was tried to play around with depth and blend options but without result.

Perhaps that a solution can be found by modifying this part code.

gl.enable(gl.DEPTH_TEST);
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

Variant below are fixes my problem, but does not give me needed result. Particles becomes transparent and overlap each other.

// gl.enable(gl.DEPTH_TEST);
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE);

So here is jsfiddle with my problem.

genpfault
  • 51,148
  • 11
  • 85
  • 139

1 Answers1

1

The problem is z-buffering and the depth test.

You're drawing quads in effectively a random depth order. So, you draw a close particle first, it draws the transparent pixels on the corners of the happy face and sets the zbuffer for those pixels. Later some particles behind get drawn but the zbuffer says not to draw them.

A few solutions, each with their own issues

  1. Sort all the transparent stuff you're drawing then draw furthest to closest.

    Plus: It works

    Minus: It's really slow, especially for thousands of particles.

  2. Use an additive or other blending mode.

    Plus: works for certain cases like fire and possibly smoke

    Minus: only works for certain cases like fire and smoke

  3. Discard transparent pixels

    Change your shader to discard transparent pixels like this

    void main() {
      float alphaThreshold = 0.1;
      vec4 color = texture2D(u_sampler, gl_PointCoord);
      if (color.a <= alphaThreshold) {
        discard;
      }
      gl_FragColor = color;
    }
    

    Plus: Works for opaque textures with 100% transparent pixels

    Minus: Doesn't truly work for anti-aliased less than 100% transparent pixels.

If you try #3 on your sample you'll see it mostly works but when particles are really large you can still see the issue on the semi-transparent pixels that still pass the alphaThreshold test. You can change the alphaThreshold to a higher number to discard more pixels and it might look better or worse.

Also just FYI: Be aware that GL has an implementation dependent limit on how large POINTS can be so your sample will not work as intended on many implementations. You might want to consider switching to quad based particles.

Community
  • 1
  • 1
gman
  • 100,619
  • 31
  • 269
  • 393
  • Thanks for a good and detailed answer! But look what i found today: here is [one of three.js examples](http://threejs.org/examples/webgl_custom_attributes_particles2.html) and it is totally what i need. Semi-transparent pixels rendering as well with normal blending. But i looked at the source code and found nothing that may sort the particles. Maybe three.js doing this automatically or maybe there are 4th solution of my problem. What do you think about it? –  Apr 26 '14 at 18:03
  • If you look at the source of that sample, line 140, you'll see `sphere.sortParticles = true;`. Set that to `false` and you'll see the same problems your jsfiddle was having. So yes, three.js is sorting the points, solution #1. I upped the number of particles from ~2500 to ~23000. With sorting off it runs at full speed, with sorting on it's 3x slower. Just pointing out like it says above, sorting is slow. Whether it's too slow is up to you. Most games don't sort every particle, rather they sort groups of particles. Like if there are 2 explosions they'd sort 1 behind the other not the particles. – gman Apr 27 '14 at 16:49