Premultiplied alpha in XNA Game Studio

It is possible to use premultiplied alpha with XNA Game Studio, but the bad news is we don't do much to help you with it.

Why not?

Yeah.  Our bad.  In fact one of my biggest regrets about the design of the XNA Framework is that we didn't do more to make this easier!

To use premultiplied alpha, you must do three things:


1 – Set The Blend State

Premultiplied alpha blending is configured like this:

    graphicsDevice.RenderState.AlphaBlendEnable = true;
    graphicsDevice.RenderState.SourceBlend = Blend.One; 
    graphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha; 

Or if you are using SpriteBatch:

    spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None);
    graphicsDevice.RenderState.SourceBlend = Blend.One; 


2 – Premultiply Your Colors

Premultiplied blending only works if all your color values are in premultiplied format. But most paint programs and image file formats do not use premultiplied alpha! So we must find a good place to apply this conversion.

For most games, both 2D and 3D, the flow of color data goes something like this:


  • Color values are edited in a paint program, then saved into a .bmp or .png file, which is built into .xnb format by the Content Pipeline, then loaded into a Texture2D object.

  • Other colors may be used to tint the texture. These can be specified as part of the vertex data, or passed as the color parameter to SpriteBatch.Draw.

  • Still more colors may be computed on the fly (eg. realtime lighting).

  • All these colors are passed into the shader, which could do all kinds of interesting things with them, but most often just multiplies them together.

  • The shader produces a final combined color, which is passed to the alpha blending operation.

There are several options for where in this process we choose to convert from conventional to premultiplied format. Two in particular I think can be sensible depending on the situation:

Convert colors at the end of the pixel shader Convert colors in a custom Content Processor
Add "result.rgb *= result.a" right before the end of all your shaders Write a PremultipliedAlphaTextureProcessor, which automatically converts all your textures to premultiplied format while they are being built
Requires custom pixel shaders Does not require custom shaders, so works with SpriteBatch, BasicEffect, etc.
Premultiplication happens after the shader has processed any tint colors, so tints are still specified in conventional, non-premultiplied format All runtime colors, including tint values, are specified in premultiplied format
Alpha blending is done with premultiplied colors, so image composition works properly, but texture filtering happens before the premultiply conversion, so alpha cutouts remain a problem All rendering uses premultiplied colors, so both image composition and alpha cutouts work nicely
Reasonably easy retrofit to existing code, or even just specific parts of that code, as long as you have custom shaders Affects everywhere you do color math, which may be a lot of places, so it can be a pain to retrofit if you don't plan this from the start


3 – Use The Right Math

Any time you do computations on color values, you need to use the right math for the type of colors you are dealing with. Some things to bear in mind:

  • Keep track of which data is in which format. Mixing premultiplied with conventional colors is a recipe for chaos!

  • This distinction is only important for colors that have fractional or zero alpha. Opaque colors are the same in both formats, so nothing changes for computations that use RGB color without alpha.

  • Conventional colors change opacity by leaving RGB alone while decreasing alpha. For instance to draw a half transparent orange sprite we would specify a tint of (255, 128, 0, 128). But with premultiplied colors, we must also decrease the RGB values, so that same tint would be specified as (128, 64, 0, 128).

  • By far the most common color operation is tinting, which is done by multiplying two colors together (for instance this is how SpriteBatch.Draw combines its texture and color parameter). Color multiplication works the same for premultiplied and conventional colors, as long as both operands are in the same format.

  • This means that SpriteBatch works as-is with premultiplied data.

  • BasicEffect diffuse lighting and vertex coloring also use color multiplication, and therefore work correctly with premultiplied data.

  • The BasicEffect specular lighting and fog computations will not work correctly with premultiplied data. If you want specular lighting or fog while using premultiplied alpha, you will have to write a custom shader.

Comments (4)

  1. Pete says:

    "Affects everywhere you do color math, which may be a lot of places, so it can be a pain to retrofit if you don’t plan this from the start"

    I was just about to ask you how cumbersome is managing changes in the alpha value fof the premultiplied methodology.

  2. n00body says:

    I have a concern with this system. What do you do when you want to gamma correct your textures? Specifically, what happens when you try to gamma correct a filtered value where the alpha was pre-multiplied with the gamma-space color?

  3. ShawnHargreaves says:

    > What do you do when you want to gamma correct your textures?

    If you are aiming for truly gamma correct rendering, this is trivial. Both filtering and alpha blending are only truly correct in linear color space, so you ideally want to do the gamma correction after all such operations are complete, in which case it makes no difference what alpha format you use prior to this.

    You could certainly have problems if you are doing something like filtering linear texture data, gamma correcting, then alpha blending in gamma space. But such a path has lots of other problems already, for instance multisampling can cause all kinds of nasty artifacts.

  4. Jordan Trudgett says:

    Hi Shawn,

    Thanks for this, it really made a big difference, which I’m very happy about! Here’s a before and after shot.

    No more ugly black pixels, and I can blit to non-integer positions with whatever rotation I like… all the goodness, none of the compromise!

    Thanks again!

Skip to main content