As of Game Studio 4.0, the default alpha blending mode is now premultiplied rather than interpolative.
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:
- 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?
- 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.
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:
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)