Alpha/Transparency Sorting, Your Z-buffer, and You

Since this comes up every three or four days, I decided to put together some visual aids.

There is often a lot of confusion with how pixels get processed in relation to the z-buffer and why sorting is important. Most importantly it can be mind-warping trying to wrap one’s head around how to ‘fix’ the cases where proper sorting isn’t possible.

Hopefully these diagrams help show the futility of it all… I mean the trade offs one has to make to get transparency looking its best in the general case.

The first image I’ll show is the ‘best case’ where all objects are drawn back to front. I’ll then discuss briefly the steps JME goes through to try to make that happen.

In JME, the opaque layer would generally be placed in the opaque bucket. All opaque layers are drawn first and within the ability to sort them, they are sorted front to back to prevent overdraw.

After the opaque bucket is drawn, the transparent bucket is drawn and it is sorted back to front. So in the ideal case it would appear exactly as in this picture.

Do note: this sorting is done at the object level and it can never be perfect. It is impossible to properly sort objects by distance in the general case even if they were just triangles. Simply imagine intersecting triangles and it’s clear there is no proper order. JME tries its best.

Next I’ll show an image of the worst case scenario to show why sorting is important and how your ‘best friend’ the z-buffer is really transparency’s worst enemy.

This is what will happen if you put all of your objects in the opaque buffer. JME will sort them front to back and you will get these strange ‘windows’ into your background.

Note: because sorting is done at the object level, you may have triangle to triangle overlap even within the same mesh if it is non-convex. Think of a glass donut where the near surface triangles are drawn before the hole’s triangles. There will be this same issue where they occlude the farther triangles. A different angle on the donut might produce the correct results depending on the order of the triangles in the mesh.

Finally, I’ll augment the worst case sorting with something like alphaDiscardThreshold (or the old alphaTest/alphaFalloff values). In this example, let’s pretend we only discard pixels with alpha = 0.

It’s better but not perfect. Any partially transparent pixels will still show the issue. Partial transparency will drive you crazy if you let it.

Bottom line:

  • back to front sorting would fix all issues
  • accurate back to front sorting in the general case is impossible
  • for purely on/off a = 1 or a = 0 transparency then a discard threshold is the best bet to mitigate sorting problems.
  • Where 0 < alpha < 1, improper sorting of triangles/pixels will always cause artifacts.
35 Likes

Fantastic, this could so do with being a wiki page with a link on the left hand side in the forums “Alpha/Transparency Sorting for dummies” :smiley:

1 Like

Yeah, I didn’t feel like dealing with the wiki after spending all the time on the images. Anyone can feel free to copy the content there if they like. I won’t object. :slight_smile:

1 Like

Fair enough, good work though :thumbsup:

1 Like

wow
So well explained even I now understand it :+1:

1 Like

Added this post to the menus as “Transparency for dummies”

2 Likes

Thanks for the kind words, guys. Glad it turned out well.

4 Likes

I would like to give you a thank you, for this wiki, I always wondered why it was not there.

If possible it would be nice to explain in detail how everything on each material work exemple the AlphaDiscardThreshold(what is the value you must use, same for all the place where you need values, no one ever though some people would wonder what does it do? what are the value to be use there?
AlphaTestFallOff? PolyOffset? How to use every kind of alpha?

1 Like

AlphaDiscardThreshold is a material parameter that controls if a pixel (fragment) is discarded because of alpha or not.

Literally:

        if(alpha < m_AlphaDiscardThreshold){
            discard;
        }

AlphaTest/AlphaFalloff don’t do anything. They used to do exactly what AlphaDiscardThreshold does but they are deprecated in OpenGL (and maybe not even available on ES? I don’t remember) so have been nerfed in 3.1. (Personally, I found this a questionable decision but I didn’t do it.)

PolyOffset has nothing to do with transparency. If you are curious about OpenGL stuff like that then google is your friend.

2 Likes

FYI: I put the pictures up on imgur as a gallery. Not entirely happy with the format there but oh, well.

2 Likes

I created a wiki article based on this:
http://wiki.jmonkeyengine.org/doku.php/jme3:intermediate:transparency_sorting

Thanks @pspeed :wink:

5 Likes
    t.setMagFilter(Texture.MagFilter.Nearest);
    t.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
    mat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
    mat.setFloat("AlphaDiscardThreshold", 0.05f);
    geo.setQueueBucket(Bucket.Translucent);

Great! transparency notes. This seems to handle thousands of transparent textures!

1 Like

Yeah, for blocky Minecraft style textures that works well.

1 Like

one note miss a ‘b’ at the begining
‘’’
ecause sorting is done at the object level
‘’’

1 Like

I fixed the typo. Thanks for letting us know.

1 Like

This is a cool thread and something that I’ve deal with. For most this won’t be a problem and on opengles 3.1 or I think opengl 330 core or higher this isn’t so much of a problem because of order independent transparency. I am a bit new to jme but it does a lot of great things.

I was just curious what’s the state of the renderer? If I remember a blog post said the backends were unified to opengl and I can see from the material system that you guys are using shaders so at least opengles 2.0

What’s the minimum opengl and opengles versions platforms, ie… android, ios, pc need to support?

1 Like

The transparency issues won’t be resolved with OpenGL 3.3 … They won’t even be resolved with Vulkan.

Currently jME 3.1 supports OpenGL 2.0 on desktop and OpenGL ES 2.0 on Android / iOS.

1 Like

With OpenGL 4 you can do Order-independent transparency.

1 Like

Sure, but I was referring to practical approaches which work for games.
You could achieve order independent transparency with depth peeling a long time ago but it wasn’t very fast.

1 Like

@Momoko_Fan I remember depth peeling from a while ago and you are right it wasn’t very fast but with gles 3.1 and floating point support in the shaders order independent transparency is pretty reasonable on that class of hardware. I forget which desktop version of gl is on the same level of gles 3.1 though.

Here’s a few articles on the topic, I was researching this for a while.

One technique from intel: https://software.intel.com/en-us/android/articles/efficient-order-independent-transparency-on-android-using-fragment-shader-ordering

the second part of the causal-effects blog

This one I haven’t actually tried but it’s been around for a while, there’s a paper on it: https://web.archive.org/web/20111123085111/http://www.sci.utah.edu/~bavoil/research/kbuffer/StencilRoutedABuffer_Sigg07.pdf

and some demo code: Humus - 3D

1 Like