Premultiplied alpha in XNA Game Studio 4.0

Premultiplied alpha in XNA Game Studio 4.0

  • Comments 19

As of Game Studio 4.0, the default alpha blending mode is now premultiplied rather than interpolative.

 

Why?

I was fed up of repeating this conversation:

Customer: Why do I see ugly borders around the edges of my sprites?

(or sometimes: Why does rendertarget blending not work like I expect?)

Me: To make that work right, you need premultiplied alpha.

Customer: Ok, how do I do that?

Me: Read this lengthy article. First, you need to run all your textures through a special content processor, which is not built into Game Studio. Then, change your color math to work a different way, using operations that are not built into the Color struct. Finally, change your blend renderstates to a custom configuration, which is not built into the SpriteBlendMode enum. Oh yeah, and you can no longer use BasicEffect specular lighting or fog on translucent objects.

Customer: Yikes! How come this isn't easier?

Me: Sorry. Our bad.

To be clear: premultiplied alpha is not a perfect panacea for all problems (Kris commented on my blog with an article listing some cons). Expert graphics programmers can be expected to understand multiple color formats, and choose the appropriate one for different situations. Game Studio 4.0 continues to support interpolated alpha blending, just like previous versions supported premultiplied alpha. So we are really just dealing with two questions here:

  1. What is the default alpha format? When a developer who does not yet understand the blend equation, color math, and texture filtering, loads a sprite and draws it to the screen, which are they using?

  2. What do more experienced developers have to do if they want something other than this default?

The second was a no-brainer. Changing alpha format was too hard in previous Game Studio versions, and should be made easier.

The first was not such an obvious decision. We felt that premultiplied alpha would be a better default, as it avoids several problems that have proven to be both common and extremely confusing. But this would be a major breaking change for people who are used to doing things the old way, and are maybe already partway through creating a game. Starting from a blank slate, premultiplied versus interpolative color math are roughly equal in complexity, but it can be painful to change format in mid stream!

In the end, two things made up our minds:

  • "If you break it, break it good"

  • We believe in the long term future and growth of our platform. If it has such a future, that means all the people using XNA today are just a tiny fraction of those who will use it in the future. Therefore it is worth causing some pain for our existing developers, if this can reduce pain for a larger number of developers-to-be.

 

What changed?

The blend state now defaults to premultiplied:

  • BlendState.AlphaBlend gives premultiplied blending
  • SpriteBatch.Begin() with no arguments gives premultiplied blending
  • If you want interpolative blending, use BlendState.NonPremultiplied

By default, the Content Pipeline now converts all textures into premultiplied format. If you do not want this, or if your source texture files are already in premultiplied format, turn off the "Premultiply Alpha" processor parameter:

Untitled

We tweaked the Color struct to better support premultiplied math:

  • Removed the Color(rgb, alpha) constructor overload, which was used to change the alpha of an existing color, because this operation does not make sense when using premultiplied values

  • Added a color * alpha operator, which is how you change the transparency of premultiplied colors

  • Added a Color.FromNonPremultiplied conversion helper

  • Instead of Color.TransparentBlack and Color.TransparentWhite, we now have a single Color.Transparent

BasicEffect, and its four new built-in sibling effects, assume premultiplied alpha in all the places where this makes a difference:

  • If you are drawing opaque objects, premultiplied versus interpolative alpha is irrelevant

  • If you are drawing translucent objects, any use of specular lighting, fog, or environment mapping requires premultiplied alpha

  • Everything else works the same with either alpha format

 

How do I upgrade my code?

For most games, only one code change is needed to switch from interpolative to premultiplied alpha. Everywhere that you used to call:

    new Color(color, alpha)   // where alpha is a byte ranging 0-255

Change this to:

    color * alpha   // where alpha is a float ranging 0-1

The bad news is you might have to change this in a lot of places! But the good news is, since we removed that Color constructor overload, you will get helpful compile errors any place you forget to do this :-)

If you have textures that encode data other than transparency into their alpha channel (for instance if you are using alpha to store a gloss map, or a displacement for parallax normal mapping), you should turn off their "Premultiply Alpha" content processor parameter.

 

How can I get the old behavior back?

If you prefer to continue using interpolative alpha like in previous Game Studio versions, you must do three things:

  • Turn off the "Premultiply Alpha" content processor parameter for all your textures and models

  • Specify BlendState.NonPremultiplied whenever you call SpriteBatch.Begin, and set this onto the graphics device before you draw other geometry

  • Change:  new Color(color, alpha)  ->  new Color(color.R, color.G, color.B, alpha)
  • Interesting stuff, I'm just now trying to upgrade a 3.1 project to 4.0 and this Color constructor thing sucks... Two things I found really confusing, 1) why couldn't you leave in a constructor that took those arguments and just built it with the new operator color*(alpha/255f)? and 2) why the Color*float operator does not work in the other direction (float*Color)?

    For (2) I would assume that you couldn't add a float operator overload for some reason?  But for other XNA classes like Vector2,3,4 you can.  Or is it a semantic thing you want to specifically enforce?

  • This change caused a lot of headache for me. In your other article you say, "But most paint programs and image file formats do not use premultiplied alpha!", which is completely correct. That alone, in my opinion, should be why non-premultiplied should be the default.

    The answer to "Customer: Why do I see ugly borders around the edges of my sprites?" is to have them export their texture using a file format (such as PNG) that supports an 8-bit alpha channel.

    Premultiplied alpha is perfect for "faking" transparency in web graphics where the background color can be assumed. For dynamic games though? It seems like a lot of trouble.

    Always enjoy reading your blog Shawn, keep up the great work (Just don't go making too many breaking changes!)

  • > The answer to "Customer: Why do I see ugly borders around the edges of my sprites?" is to have them export their texture using a file format (such as PNG) that supports an 8-bit alpha channel.

    Except that doesn't solve the problem  :-)

    Premultiplied alpha has nothing to do with faking transparency for web graphics  (although most web based composition systems such as Flash or Silverlight do all their rendering using premultiplied colors, fo the same reasons that XNA 4.0 does).

    Regardless of whether you use an image format with an 8 bit alpha channel or not, if you choose something other than premulitplied color format, you will suffer from incorrect interpolation any time you filter an image, not to mention finding it fundamentally impossible to make any kind of layered image composition produce sensible results.

    Sure, it can be painful to convert a large existing project from one color format to another. That's why you should use premultiplied alpha right from the start!

  • Is there working code for drawing sprites with transparent areas (Magenta)? Everywhere I have seen for XNA 4.x actually doesn't run, (the transparent color is not drawing as transparent).

Page 2 of 2 (19 items) 12
Leave a Comment
  • Please add 3 and 1 and type the answer here:
  • Post