Blend Modes
For our effects to look right, we need to use the appropriate blend mode. I'll focus on OpenGL, but everything stated here holds for DirectX or any other low-level 3D API.
The last stage of the OpenGL 1 pipeline is to combine the current color at the pixel buffer with the color of the incoming fragment. The examples for glBlendFunc in OpenGL ES 1.1 docs states that:
Transparency is best implemented using glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) with primitives sorted from farthest to nearest.
This is unfortunate, as compositing images with this blend mode is limited and error prone. (Working naïvely with this blend mode might cause dark halos around objects)
Consider a pixel represented by: (Rs,Gs,Bs,As) which we would like to blend with the existing pixel: (Rd,Gd,Bd,Ad) where each pixel component is in the [0,1] range.
OpenGL suggests the following blending: (using the red channel as an example)
Rout = As*Rs + (1-As)*Rd
Notice that all components are always lower than or equals to one -- We cannot lighten the scene, only darken it. This blending mode is good for occluding (see the world through pink glasses), but cannot be used to lighten a scene.
To illuminate (use a flashlight over something for example), we might have chosen to do:
glBlendFunc(GL_SRC_ALPHA, GL_ONE)
That blends as follows:
Rout = As*Rs + Rd
Where we only add light to scene and control the amount of light added with the source's alpha channel. However, again, as all components are always non-negative we can only lighten the scene, disallowing occlusion.
To accommodate for both needs, Porter & Duff in a 1984 paper (Computer Graphics Volume 18, Number 3 July 1984 pp 253-259) suggest using:
Rout = Rs + (1-As)*Rd
or:
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)
That allows us achieve both occlusion and illumination:
- Occlusion is achieved by premultiplying the RGB channels with the alpha channel: Rs=Rs*As for every pixel in the original texture. This makes the above method identical to the method suggested in the OpenGL docs, and saves a runtime multiplication.
- Illumination is achieved by not premultiplying the texture, and using the alpha channel as the amount of light to add to the scene
A picture is worth a thousand words. In the following example both particles are rendered with the same alpha channel (shown on the right) in the same blend mode (mentioned at the bottom). Notice how we're able to achieve smooth occlusion on the right particle, while being able to lighten the background on the circumference of the left particle:

If I would have had to guess why OpenGL authors chose the less useful blending equation as their example, I would guess that it's because most (if not all) graphic editors don't premultiply alpha, and this is the correct way to achieve the expected particle (on the right).