OpenGL: Transparency

If an object is visible through another occluding (partly) translucent object, we observe the effect of transparency. In OpenGL you achieve this with so called Blending. In general an opqaue object occludes every other object that is farther away behind it in the viewing direction. The Z-Buffer holds the current depth value of every pixel being drawn in the framebuffer at a specific position. If a pixel is about to be drawn, it has to pass the depth test. Its depth value has to be smaller than the one currently stored in the Z-Buffer. Otherwise the pixel is discarded. This is true for transparent objects as well, but they don’t replace the current color in the framebuffer. They mix with it. The resulting color is computed by applying a mixing function (blending function). You need the following setup in OpenGL:

  1. glEnable(GL_BLEND)    // enable blending
  2. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)    // choose blending function

Disable everything with glDisable(GL_BLEND).
Let’s have a closer look at the blending function. The one pixel that is being drawn (transparent) is called the source pixel. The other pixel, that is already in the framebuffer is called the destination pixel. The blending function defines two parameters: GL_SRC_ALPHA gives the alpha value a of the source pixel denoting the amount of transparency (rgba). GL_ONE_MINUS_SRC_ALPHA is 1 – GL_SRC_ALPHA (1 – a). The mixed color is given by the sum of the source pixel’s color multiplied by a and the destination pixel’s color multiplied by (1 – a). Consider a pure red object as example with rgba = {1.0, 0.0, 0.0, 1.0} which is occluded by a transparent white object with rgba = {1.0, 1.0, 1.0, 0.2}. Attention: rgba defines the opacity rather than transparency in a: transparency = 1 – opacity. An object with a = 0.0 is therefore completely transparent. In our example we get the final mixed color as follows: rgb = {1.0, 1.0, 1.0} * 0.2 + {1.0, 0.0, 0.0} * {1 – 0.2} = {1.0, 0.2, 0.2}. This is a slighter red tone that is 80% visible through the white occluder. In terms of mathematics the applied blending function is just a linear interpolation.
Tip: OpenGL offers other mixing functions as well, but the one here is mostly used for transparen effects.

Rendering order

The setup described above gives the basis for rendering transparent materials. But it is not sufficient for every situation. Rendering result depends directly on the order objects are done in the rendering loop. One reason is the Z-Buffer. Is a transparent object drawn first, its depth values prevents all farther lying objects from being drawn afterwards as they do not pass the depth test.
Another reason is the mixing function. Its parameters depend on source- and destination pixel which are directly linked to the rendering order. The source pixel is drawn right at this moment and gives the transparency value. The destination pixel is already stored in the framebuffer and the one being blended. We only get the correct resulting color from the mixing function if the transparent object comes AFTER the farther lying object in rendering loop.
The easy solution for this is as follows: At first, disable depth writing for all transparent objects of the scene. Farther lying objects are thus always drawn – independent of their rendering order. In OpenGL its done by calling glDepthMask(FALSE). Attention: Z-Buffer reading stays enabled. Second, all objects of the scene are distinguished into opaque and transparent materials. Now the rendering loop takes all opaque objects first and the transparent objects afterwards. Thus we provide the rendering order we need and the opaque objects are correctly blended by the later drawn transparent objects.

Conclusion

This approach can be done at low costs for the sorting and is appropriate for most situations. It gets difficult when transparent objects occlude each other and might lead to wrong rendering results. In order to solve this you need to sort transparent objects themselves by their depth value (in z-direction). This sorting is not trivial and performant approaches are currently subject to cg research. An algorithmic approach without sorting is depth peeling.

Know more: Depth sorting alpha blended objects

Author:    Michael Keutel | 30.05.2013