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)
  • What happens if a Texture is loaded directly from a stream in this case? In this case, I assume I'll either need to pre-multiply the alpha in my PNGs or disable it in every sprite batch?

  • To draw a texture from a stream i use this

    Sprite.Begin(SpriteSortMode.FrontToBack, BlendState.NonPremultiplied);

    so yes, you need Non-Premultiplied

    "Turn off the "Premultiply Alpha" content processor parameter for all your textures and models" - why do not turn off it by default ^^

  • Thankyou, Shawn!

    I know all about premultiplied alpha and how to implement it - but up until now it was always easier to just manually re-jigger my art by hand!

    Now I don't have to do either! It's fantastic!

  • > What happens if a Texture is loaded directly from a stream in this case? In this case, I assume I'll either need to pre-multiply the alpha in my PNGs or disable it in every sprite batch?

    Correct. If you don't go through our Content Pipeline, you have to handle the format conversion yourself, or just not use premultiplied alpha.

  • > "Turn off the "Premultiply Alpha" content processor parameter for all your textures and models" - why do not turn off it by default

    Because that would defeat the whole point of making premultiplied alpha be the default format!

    Of course, some people will want different formats depending on their situation. That's why we give the option to change this. But for most people, in most situations, premultiplied alpha works the best, which is why we made it the default.

  • Does the texture processor have support for pre-multiplied DXT formats and selecting these vs normal alpha DXT formats?

  • > Does the texture processor have support for pre-multiplied DXT formats and selecting these vs normal alpha DXT formats?

    On the contrary, we removed the DXT2 and DXT4 formats entirely from Game Studio 4!

    These were never 'real' texture formats: they make no differene at the DDI, driver, or hardware level. They were just an attempt at tagging the texture with some additional metadata information describing the meaning of the data inside it. But this is rather silly, because no other texture formats have similar metadata tag (there aren't multiple versions of 32 bit color, for instance), plus this mechanism was insufficient to properly describe the contents of any texture (what about normalmaps? Gloss maps? etc).

    So in Game Studio 4.0, a texture is just a texture, holding numbers that can be read by the GPU. If you want to store metadata indicating what these numbers mean, it is up to you to store this yourself in some app specific format.

    Note that DX10+ works the same way.

  • OK, I was always under the impression that the compression code behaved differently based on the pre-multiplied formats...

  • Thanks Shawn for all these articles describing the XNA 4.0 changes in such clear language, and especially giving the *reasons* for these changes!

    Right now I am staying with XNA 3.1 since these changes would break too much of my existing code, but I do have the very strong feeling that all these changes (color format, rendertargets, renderstates, ...) make a lot of sense and are further adding a lot of both ease as well as professionalism to XNA! :-)

  • Shawn, can you recommend any reading on the subject of color math? I'm very interested in learning the theory behind all of this. Thanks!

  • > can you recommend any reading on the subject of color math?

    Computer Graphics: Principles and Practice (by Foley, Van Dam, etc) is one of the classic texts of the computer graphics world. It's a little dated now, and doesn't talk about premultiplied alpha, but has some good background information about different color spaces and chroma representation.

    About premultiplied alpha specifically, Tom Forsyth has a great article on his blog (http://home.comcast.net/~tom_forsyth/blog.wiki.html). And here is the original paper on this stuff: http://portal.acm.org/citation.cfm?id=808606. I've blogged about this in the past, too.

    Once you learn to read them (not always easy if you don't have a mathematical/academic background!) SIGGRAPH papers are an awesome source of theoretical background on just about everything.

  • Now that PMA is standard, does it mean that if I set my tint_color.a=0 in the spritebatch.draw while letting tint_color.rgb normal then I would get an additive blending?

  • > Now that PMA is standard, does it mean that if I set my tint_color.a=0 in the spritebatch.draw while letting tint_color.rgb normal then I would get an additive blending?

    That's exactly what happens with premultiplied alpha, yes.

  • >Correct. If you don't go through our Content Pipeline, you have to handle the format conversion yourself, or just not use premultiplied alpha.

    Can you (or anyone) give a hint as to how one would go about processing a Texture2D.FromStream texture for premultiplied alpha?

  • To answer my own question, if anyone else wants to  use premultiplied alpha in XNA4, but not load their textures from the content pipeline, I pasted a (probably inefficient) solution here:

    http://codepaste.net/6h1pgt

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