0
votes

What is the best way to render a mixture point sprites and 3D objects?

Say for example, I wanted to model "space dust" particles as point sprites. In space I could have large asteroid objects (cubes for simplicity) and the space dust particles could be in front of or behind the asteroid cubes.

I am stuck with 2 scenarios:

  1. I render the space dust point sprites first, use gl_DepthMask(GL_TRUE) and then render the asteroid cubes (space dust is always behind the asteroids)
  2. I render the asteroid cubes first, use gl_DepthMask(GL_FALSE), render the space dust point sprites and set gl_DepthMask(GL_TRUE) (space dust always appears in front of the asteroids)

I am currently using a single particle emitter vertex shader that draws a load of particles with gl_DrawArrays() at random (x,y,z) positions.

How can I render a load of particles so that they will appear both in front of and behind the asteroid cubes?

If my particles were individual quads then I could sort the order of Z rendering but I have no idea how to achieve this behaviour using gl_DrawArrays(), GL_POINTs and my vertex shader.

Should I perhaps be splitting up the particles into sets of arrays - for example would I have to do something like this:

  1. draw an array of particles that are behind all asteroids
  2. draw asteroid furthest away
  3. draw another array of particles that appear in front of the above asteroid but behind the next closest asteroid
  4. draw front-most asteroid

If I have a lot of asteroids, wouldn't this be terribly inefficient?

Surely OpenGL has a better way of achieving this than what I am suggesting above?

Update: Trying Thomas' suggestion:

My cube and particles are rendered with the following blend/depth calls:

  // First render opaque object(s)
  glEnable( GL_DEPTH_TEST );
  glDepthMask( true );
  g_ObjCube1.Render();

  // Now render sprites with particle emitter - sprites should appear
  // behind and in front of above cube
  glEnable( GL_POINT_SPRITE );

  // Stop overlapping particles from interfering with each other
  //glDisable( GL_DEPTH_TEST );  <- NO! Must leave depth test enabled
  glDepthMask( false );

  glEnable( GL_BLEND );
  //glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
  glBlendFunc( GL_SRC_ALPHA, GL_ONE );    // Doing the same as above
  glBlendEquation( GL_FUNC_ADD );
...
  glDrawArrays( GL_POINTS, 0, NUM_PARTICLES );
...
  glDepthMask( true );
1

1 Answers

1
votes

I'm assuming that your particles have some transparency, otherwise there wouldn't be a problem.

And then the answer is the same as for all transparent objects. First render opaque objects (asteroids) first (with depth buffering, in any order). Then render all transparent objects in back-to-front order. This ordering will (as far as I'm aware) need to be done on the CPU.

Alternatively, depending on what effect you're aiming for, you could use additive blending for the particles (glBlendFunc(GL_SRC_ALPHA, GL_ONE)). Then rendering order doesn't matter, because addition is commutative. Be sure to disable depth writing for this one.

As another alternative, using alpha testing (true/false) instead of alpha blending would let you render your particles in any order. Fully transparent pixels don't get written to the depth buffer in this case. When using this technique, you cannot have partially transparent areas though.

This article is a great reference.