How Shawn learned to stop worrying and love premultiplied alpha

Originally posted to Shawn Hargreaves Blog on MSDN, Friday, April 9, 2010

I like to think I know what I'm doing with computer graphics, so I find it embarrassing that it took us four versions to get something as fundamental as alpha blending right.

To cut a long story short, I didn't properly understand alpha blending until way too recently :-)

(but in my defense, it seems like many other people don't understand it either)

To leave the long story long:

Graphics programming, especially for games, is an interesting mix of science, art, and Rube Goldberg contraption. Some things can be mathematically proven to have a single correct solution, so you'd be crazy to do them any other way. Other times we can achieve great results from ad-hoc hacks that only make sense for a specific game or piece of hardware.

When I started making games, the focus was very much on the hack side of things. Most projects began with being given a piece of hardware, and told to go off and make it do something cool. Few people had formal backgrounds in math or computer graphics.

Over time, the focus shifted toward properly understanding the theory behind what we were doing. Many projects began with being given a stack of SIGGRAPH papers, and told to go off and implement the techniques described therein. This often left those of us who grew up figuring things out by trial and error scrambling to relearn old tricks.

So how did I learn about alpha blending?

The first time I encountered the concept of blending colors was in college, where I wrote a game programming library called Allegro. Working with 8 bit palettized colors, this included a draw_trans_sprite() function which used a 64k lookup table to precompute every possible combination of two color values. This could achieve many varied effects just by changing what went into the table, but with only 256 colors to choose from, the quality of the results was, well, kinda dubious :-)

The first time I experienced proper hardware alpha blending was on the Nintendo 64, where we used a mixture of additive and interpolative blending, mostly for special effects. Textures were extremely small, usually just 4 bpp, and drawn by hand with Deluxe Paint. If we saw filtering or blending artifacts, we just asked the artist to change the offending texture.

The first time I ran into alpha blending problems was on Playstation II, where the artists used scanned photos as a source for creating 4 and 8 bpp palettized textures. Thanks to the unholy combination of filtering and interpolative alpha blending, we got ugly fringes around the edges of our tree billboards. I fixed this by using our equivalent of a custom content processor to automatically adjust the RGB of transparent palette entries, changing them to match the average color of the texture as a whole. Problem solved (or so I thought...)

The second time I ran into alpha blending problems was when we moved from Playstation II to the original Xbox, and switched from palettized textures to the more efficient DXT compression. DXT1 only supports transparent pixels where the RGB is zero, so my automatic fixup was no longer possible and the black fringes returned. I 'fixed' this by switching to DXT5 format wherever possible, plus using alpha test to skip drawing the problematic borders of textures for which DXT5 would use too much memory. This was a delicate balancing act. If the alpha test threshold was too high, the trees appeared jagged and hard edged, but it was too low, they had black borders. Somewhere in between, both problems still existed, but neither was too obvious. Problem solved? (kinda, but it made me feel dirty...)

The third time I ran into alpha blending problems was when I optimized particle rendering by drawing into a lower resolution rendertarget. Rendertarget composition is fundamentally impossible when using interpolative alpha, but I didn't know this at the time. So I sat down with pen and paper, did some algebra, and figured out the blend math necessary to get the result I wanted. It turns out that I independently reinvented premultiplied blending, but I failed to generalize this or realize how useful it could be for other things!

A few years later, I read Tom Forsyth's excellent article about premultiplied blending. "Aha!" quoth I. "So that's what that crazy thing I used for the particles is called. And wow, it seems pretty handy for other stuff too." Then I promptly forgot all about it.

A few more years later, I left Climax, left England, moved to Seattle, joined Microsoft, and started working on XNA. Having forgotten Tom's article, it never occurred to me (or I guess to anyone else here) that this might be something worth including in our API.

Shortly after we shipped XNA v1, I saw the first "wtf is up with these crazy borders around my sprite?" question on our forums. In the middle of typing up a lengthy "here's the laundry list of hacks I used to work around this problem in MotoGP" reply, I remembered, "wait, didn't Tom know a better way to fix this?"

After several years of getting this same question, pointing people toward Tom's article, and watching the ensuing confusion because it was too hard to do this with our API, we decided it was time to fix this mistake once and for all.

Enter Game Studio 4.0.

And they all lived happily ever after.

(touch wood)

Makes me wonder what else is out there that I don't know but should?

Blog index   -   Back to my homepage