BlendingTutorial

From TDN

Blending is an operation that can occur under many different circumstances. For example:

  • When multiple textures are applied in a single pass the textures are blended together.
  • When geometry is drawn with an alpha value on the vertices
  • When a texture is drawn that has an alpha channel

Pretty much any time you are drawing something to the screen, some kind of blending is occuring, and understanding what is going on will help you to be a better 3d programmer, and let you do some interesting effects with fixed-function operations. For the purposes of this document, I will be using OpenGL, however the same concepts apply to Direct3D.

Contents

The Trivial Case

Most people only notice blending when drawing something they know has an alpha channel. Blending doesn't actually have to include transparency at all. All blending does is figure out what pixel value the framebuffer should have at a given location. So you could actually "blend" a fully opaque shape to be fully opaque. How, you ask? First I will state the blend function, then I will explain it. If you want to draw a rectangle on the screen and have it be opaque, and you (for whatever reason) think you should enable blending, this is the blend function you would want:

glBlendFunc( GL_ONE, GL_ZERO );

What Does This Mean?

If you ever looked up the glBlendFunc function you probably saw some crazy chart that you couldn't make heads or tails of and eventually just said, "screw it," and moved on. All the two parameters to the default blending function stand for are constants which are used to modify the source and destination pixels of the framebuffer. So lets look at the previous example. In the previous example I specified GL_ONE for the source factor and GL_ZERO for the destination factor. This is the normal, somewhat confusing formula from the OpenGL reference:

Rd = min(kR, Rs * sR + Rd * dR)
Gd = min(kG, Gs * sG + Gd * dG)
Bd = min(kB, Bs * sB + Bd * dB)
Ad = min(kA, As * sA + Ad * dA)

The source factor is (sR, sG, sB, sA), the destination factor is (dR, dG, dB, dA). In the example, the source factor is GL_ONE, and the destination factor is GL_ZERO, but what do those mean? This table is what describes the values of the constants.

Image:GlBlendFunc.f2.gif

As you can see, the value of GL_ONE is (1, 1, 1, 1) and the value of GL_ZERO is (0, 0, 0, 0). So what does this mean with regard to the example? Lets start substituting constants!

Rd = min(kR, Rs * 1 + Rd * 0)
Gd = min(kG, Gs * 1 + Gd * 0)
Bd = min(kB, Bs * 1 + Bd * 0)
Ad = min(kA, As * 1 + Ad * 0)

This can be further simplified...

Rd = min(kR, Rs)
Gd = min(kG, Gs)
Bd = min(kB, Bs)
Ad = min(kA, As)

Now you'll note that we have 4 constants which are undefined: kR, kG, kB, kA. where mc is the number of red, green, blue and alpha bits. The number of bits per color is defined in the PIXELFORMATDESCRIPTOR, but the bottom line is that it clamps the color to the maximum value allowed by the color depth. So if you are in 32-bit color, than for all colors (R, G, B, A) legal values are between 0-255. If you want to think of it in floating point terms, which I prefer, it is 0.0-1.0. So effectivly our sample blend works out to this:

Rd = Rs
Gd = Gs
Bd = Bs
Ad = As

So what does that mean? It means that the value of the pixel in the framebuffer will be set to exactly the value of the incoming pixel. That was a lot of explanation for that, wasn't it. Before you get all up in arms, the point here was just to understand what the constants in the table mean, and to see how they are used.

The Most Common Blend

By far the most common blend you will see is GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA. This is the standard blend that is used when you are drawing an object with transparency onto the screen. Using what we learned above lets break down this blend. We already established that this was the base equation:

Rd = min(kR, Rs * sR + Rd * dR)
Gd = min(kG, Gs * sG + Gd * dG)
Bd = min(kB, Bs * sB + Bd * dB)
Ad = min(kA, As * sA + Ad * dA)

We also established that we can cut out the min and be left with:

Rd = Rs * sR + Rd * dR
Gd = Gs * sG + Gd * dG
Bd = Bs * sB + Bd * dB
Ad = As * sA + Ad * dA

So what are the values of sC and dC? Checking the chart

sC = (As/kA, As/kA, As/kA, As/kA)
dC = (1, 1, 1, 1) - (As/kA, As/kA, As/kA, As/kA)

This can be simplified as well to:

sC = (As, As, As, As)
dC = (1, 1, 1, 1) - (As, As, As, As)

So substituting in we get:

Rd = (Rs * sA) + (Rd * (1 - sA))
Gd = (Gs * sA) + (Gd * (1 - sA))
Bd = (Bs * sA) + (Bd * (1 - sA))
Ad = (As * sA) + (Ad * (1 - sA))

Essentially, the contribution of the source pixel depends on how transparent the source is, and the contribution of the destination pixel does as well, only inversely.

Going Further

The blending equation in the fixed pixel pipeline isn't limited to

Rd = min(kR, Rs * sR + Rd * dR)
Gd = min(kG, Gs * sG + Gd * dG)
Bd = min(kB, Bs * sB + Bd * dB)
Ad = min(kA, As * sA + Ad * dA)

There are universally supported extensions to further tweek blending, in all cases, though, the output is clamped to the range (0.0 - 1.0).

EXT_blend_minmax

When this extension is used it lets you change the blend equation to something other than the default equation. It adds support for three new equations: add, min and max.

The default equation can be stated generally as
Cd = min(kC, Cs * sC + Cd * dC)
, so the new equations are:
  • Add - Cd = (Cs * sC) + (Cd * dC)
  • Min - Cd = min(Cs, Cd)
  • Max - Cd = max(Cs, Cd)

To set this equation you would use the glBlendEquationEXT function with GL_FUNC_ADD_EXT, GL_MIN_EXT, or GL_MAX_EXT.

EXT_blend_subtract

The subtract extension adds two new equation types for blending: subtract and reverse-subtract

  • Subtract - Cd = (Cs * sC) - (Cd * dC)
  • Reverse-Subtract - Cd = (Cd * dC) - (Cs * sC)

To use these blend equations you would call glBlendEquationEXT with GL_FUNC_SUBTRACT_EXT, or GL_FUNC_REVERSE_SUBTRACT_EXT.

EXT_blend_equation_separate

This extension allows you to specify separately the equations for the color and alpha blending. Basically, insted of using glBlendEquationEXT, you use glBlendEquationSeparateEXT and specify the same constants you would use for glBlendEquationEXT.

EXT_blend_color

In the blending value chart above it lists values for all the standard values for sC and dC. This extension adds some new ones.

parameter(fR, fG, fB, fA)
GL_CONSTANT_COLOR_EXT(Rc, Gc, Bc, Ac)
GL_ONE_MINUS_CONSTANT_COLOR_EXT(1, 1, 1, 1) - (Rc, Gc, Bc, Ac)
GL_CONSTANT_ALPHA_EXT(Ac, Ac, Ac, Ac)
GL_ONE_MINUS_CONSTANT_ALPHA_EXT(1, 1, 1, 1) - (Ac, Ac, Ac, Ac)

The values of (Rc, Gc, Bc, Ac) are set using the glBlendColorEXT function.

Conclusion

That is a brief overview into what exactly that crazy table does and hopefully it allowed you to at least understand what was going on during a blend.

-- Main.PatWilson - 30 Jan 2005