On the Back of a Napkin: Part 4

This article originally appeared in Dev.Mag Issue 5, released in July 2006.

Transparency effects in games are very important, not only because they make games look good, but also because they provide a unique set of problems and opportunities for smart designers to exploit. This is because transparency is implemented using a very versatile blending system, which can be put to use in tons of different ways.

Transparency, the first method

Back in the day when people manually blitted their sprites to screen memory (if you’re too young to know what blitting means, Google can help with that curiosity), they used to pick a specific colour in the sprite and simply not draw anything to the screen when they encountered that colour. Thus a technique called chroma keying was born!

Nowadays graphics APIs support chroma keying without the whole manual pixel colour check thing, which to be perfectly honest was a bit annoying to do. However, chroma keying does have a downside in modern applications: Things like bilinear filtering (remember last month’s texture filtering article?) will “blur” individual pixel colours together, so you’ll end up with the edge pixels in your texture having some of the chroma colour bled into them, giving you a nasty “halo” around your transparent object.

Chroma keying

Chroma keying

Translucency and the alpha channel of doom!

Hopefully many of you will have heard of the idea of “alpha”. When we talk about textures and transparency, alpha refers to an extra channel per pixel. We already have red, green and blue channels that define the colour of a specific pixel in a texture or sprite, if we add another channel to that we can really start doing some funky things.

The simplest implementation of alpha is single-bit alpha. That means that each pixel has an alpha value of either 1 or 0 which means it’s either on (visible) or off (not visible). This may sound similar to chroma keying, but the major difference is in how the alpha information is used by graphics APIs.

Before we tell whichever API we’re using (GL, DirectX, or any engine that uses either of those) to use a specific texture while it’s rendering, we can specify a blending operation for it to perform while it’s using that texture. Essentially blending is all about controlling the final colour value of a pixel on screen by multiplying the source colour information (the colour that the system is trying to make the pixel, gotten from a sprite or texture) and the destination colour information (the colour that the screen pixel is already) in all sorts of different ways. The usual blending operation is to get the final colour on screen by multiplying the source colour by its alpha value, and adding the destination colour multiplied by 1 – the source’s alpha. This gives us something similar to what you get by looking through a piece of coloured glass: The colour behind the glass is “let through” by how transparent the glass is, while also being tinted by the how un-transparent (opaque) the glass is. This means that an alpha value of 0 doesn’t change the onscreen colour at all (ie: fully transparent, perfect glass) while an alpha of 1 doesn’t let through any original onscreen colour at all and we only see the texture colour (ie: totally opaque, like a wall).

Now, if we give our alpha channel more bits, we make it possible for the alpha to have more values than just 0 and 1. This gives us wonderful, smooth, gradiated translucency, just like our coloured glass example. Imagine looking through a red pane of glass that’s half-transparent at a green field: Our source (the glass) has a colour value of S{red = 1, blue = 0, green = 0, alpha = 0.5} – this is called RGBA notation, I’ll let you guess why; The grass behind it is represented by D{0, 1, 0, 1} because it’s completely green and also completely opaque! So, using the blending operation we described above, we get our final colour by adding S multiplied by its alpha to D multiplied by 1 – S’s alpha (this is called giving the inverse of a value, subtracting it from its possible maximum value).

So, F	= (S * Sa) + (D * (1 - Sa))
	= (S * 0.5) + (D * (1 - 0.5))			: Substituting Sa = 0.5
	= (S * 0.5) + (D * 0.5)				: Calculating 1 - 0.5
	= ({1, 0, 0} * 0.5) + ({0, 1, 0} * 0.5)		: Substituting S and D's colours
	= {0.5, 0, 0} + {0, 0.5, 0}			: Multiplying out
	= {0.5, 0.5, 0}					: Adding the colours together

So our final value for F is {0.5, 0.5, 0}, giving us a half-red, half-green, mucky brown colour. Exactly what you would see if you looked through a red glass window at a green field, yay! Thankfully you don’t have to do this type of calculation all the time, your graphics card is doing it millions of times per second for every pixel on screen, more yay!

Alpha blending

Alpha blending

Alpha blending

Hopefully some of you will have realised that you need to have drawn the grass before drawing the red glass, otherwise all you’ll see is the grass (if you can’t figure out why, redo the calculation above and switch S and D around, to see what you get). This is called transparency ordering and is one of the nastier problems of 3D graphics, we’ll get to it later after we’ve discussed other nifty things like Z buffering.

Other uses of blending

As I mentioned earlier, blending is done by your graphics card and defined by the blending operation you choose. The blending operation we’ve gone over so far is only one of many different possibilities. You define a blending operation by telling your API what values to multiply the source and destination colours by, so the operation we looked at above is blend(source alpha, inverse source alpha), this is the setting used to get “normal” transparency. Additive transparency is given by blend(source alpha, one) because we want the screen colour to be unchanged, we just want to add our source colour “on top” of it. The re-rendered blurred textures that give HDR (High Dynamic Range) effects their characteristic over-brightness are additively blended onto the original scene. You can even “subtract” colours from an image using blending!

There are many different blending values that you can play around with: Destination alpha, inverse destination alpha, source/destination colour, inverse source/destination colour, maximum, one, zero, etc. Experiment and see what kinds of effects you can create. Blending is extremely versatile, some people even use blending to achieve advanced stencil and lighting effects, all it takes is a little thinking outside the box.

About dislekcia

Danny Day still enjoys telling people he's a game designer far too much. He has yet to apologise for accidentally staring Game.Dev all those years ago (some believe he never will) and currently runs QCF Design in the hope of producing awesome games that you'll enjoy playing. Don't talk to him about education systems, procedural generation or games as art if you value your time. [Articles]