1
votes

I'd like to draw semi-transparent primitives with GLKit. I already disabled depth testing via glDisable(GL_DEPTH_TEST) and enabled blending via

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

Now for large alpha values (e.g. 0.9), I need to draw the farthest triangles first so they don't appear above nearer triangles. This is also stated in the documentation for glBlendFunc:

Transparency is best implemented using blend function (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) with primitives sorted from farthest to nearest. Note that this transparency calculation does not require the presence of alpha bitplanes in the frame buffer

And in the OpenGL transparency FAQ:

When using depth buffering in an application, you need to be careful about the order in which you render primitives. Fully opaque primitives need to be rendered first, followed by partially opaque primitives in back-to-front order. If you don't render primitives in this order, the primitives, which would otherwise be visible through a partially opaque primitive, might lose the depth test entirely.

But as the viewing angle can change (when I change the render effect's modelViewMatrix), the farthest-to-nearest order depends on the current viewing angle.

Is there a way to have OpenGL automatically render my primitives in the right order, and if not, what would be the best way to proceed? I think that calculating the z-position of (depending on the current viewing angle) and ordering a few thousand triangles for each frame (or, more precisely, each time the viewing angle changes, which can be each frame) would be too cost intensive, especially as I'd need to push this order to the GPU for each frame.

Note that I'm not concerned with using textures (just colored vertices).

2

2 Answers

4
votes

There's no automatic solution because there's no robust real-time solution due to the problem of mutual overlap. If you'll forgive the ASCII art, consider four quads arranged as:

     +--+             +--+
     |  |             |  |
+---------------------|  |------+
|                     |  |      |
+---------------------|  |------+
     |  |             |  |
     |  |             |  |
     |  |             |  |
+----|  |-----------------------+
|    |  |                       |
+----|  |-----------------------+
     |  |             |  |
     +--+             +--+

There is no correct ordering. You'd need to split at least one of the quads into two — which is what an ahead-of-time scheme like building a BSP tree would do. That's why depth buffering exists; to solve this problem per-pixel on the basis that most geometry is opaque.

So the best ways to proceed are:

  • build a BSP tree for your transparent geometry (for complete accuracy);
  • build a per-object BSP tree for transparent geometry, sort objects back to front (for pretty good accuracy);
  • require that objects have only more-or-less convex transparent segments, so that you'd end up with a BSP tree with a single leaf were you to build one, sort back to front (for reasonable accuracy, depending on your art);
  • use additive blending and draw in any order with depth writes off, after having drawn all opaque geometry (for a different look but no ordering concerns).
4
votes

First of all, no, there is no way to have OpenGL do this for you. You'll have to manually sort your transparent geometry, based on the current camera and model position (i.e. the modelview matrix).

To implement this, in real-time implementations you don't usually sort individual triangles, but logical subsets thereof (meshes, submeshes). Problem cases then become:

  • Non-convex geometry in a single rendered element. Usually solved by splitting the geometry up manually beforehand, or ignoring the artifacts.
  • Intersecting geometry of different elements. Usually not solved.

Performance-wise it is important that, while constructing your scene, you split up your elements in (partially) transparent v.s. non-transparent. This way you avoid large sort operations, as you only need to sort the transparent geometry.

Advanced techniques

For a completely different approach, look up depth peeling and similar techniques. When done right, those techniques have a lot to offer. However, I don't know of any OpenGL ES titles actually implementing them. That doesn't mean it's not an option though.

However...

If you are not very familiar with 3D programming in general yet, I'd strongly advise to stick with the manual sort and try to reduce the artifacts to a minimum. This way you can make an informed decision whether such an implementation is sufficient or not for your particular case.

If nothing else, implementing this stuff will automatically grant you the superhuman ability to immediately spot (and be hugely distracted by) transparency-related rendering issues in every videogame you´ll ever play from now on. A marvelous skill to have.