转载自:https://developer.nvidia.com/content/alpha-blending-pre-or-not-pre
Alpha Blending: To Pre or Not To Pre
![](https://developer.nvidia.com/sites/default/files/styles/main_image/public/akamai/gamedev/mandrill_340x190.jpg?itok=RUrYceL-)
Alpha Blending is a small--but important--part of virtually every 3D application. Conceptually, alpha-blending is used to communicate the transparency of a surface. Generally, consumer applications (games) tend to use RGB to communicate the color of the underlying surface, relying on the alpha channel to indicate the "opaquness" of that color. More specifically, when alpha blending is enabled in the pipeline, developers tend to use this form for their blending:
DestinationColor.rgb = (SourceColor.rgb * SourceColor.a) + (DestinationColor.rgb * (1 - SourceColor.a));
In old, fixed functionality, this would be known as "SourceAlpha, InvSourceAlpha"; also known as "post-multipled alpha." However, this form of alpha blending suffers from a critical flaw: it results in the wrong color in many cases! The simplest of these cases can be illustrated with a simple, two pixel image:
![One red pixel with red = 1 and alpha of 1, one green pixel with green = 1 and alpha 0.10](https://i-blog.csdnimg.cn/blog_migrate/545ecd5af2b64c01c49008182518b7ea.png)
Consider the above image, whose resolution is 2x1 pixels. The artist wanted to communicate that there was a red, opaque pixel adjacent to a green pixel that would just give the slightest tinge of green to objects behind it. However, something interesting happens when we generate the next mipmap level, the 1x1 level. The result is probably surprising; the resulting mipmapped texel is this:
![RGBA = (0.5, 0.5, 0, 0.55)](https://i-blog.csdnimg.cn/blog_migrate/5288e9c70d8dc86ebefd376eb58e212c.png)
As we approach this mipmap level, we will get a very different result than when working with the 2x1 level--entirely because we decided to use postmultiplied level. You can see this in the following images:
![The canonical mandrill face.](https://i-blog.csdnimg.cn/blog_migrate/d4e7ea0ee10d428f47d02c111018fdc0.png)
![Our 2x1-rg image from above post-blended atop the mandril's face.](https://i-blog.csdnimg.cn/blog_migrate/6dc2ab4385e4b66dff9d561131208887.png)
![Our 1x1-rg image (the next coarser mipmap) post-blended atop the mandril's face.](https://i-blog.csdnimg.cn/blog_migrate/d32918e111bb91ece278910ad60f545d.png)
Enter Pre-multiplied alpha
With pre-multiplied alpha, we multiply the texture components by the alpha component first, before storage. We also modify the blend function, changing SourceColor.a to One:
DestinationColor.rgb = (SourceColor.rgb * One) + (DestinationColor.rgb * (1 - SourceColor.a));
Using pre-multiplied alpha, our original texture would look like this:
![Our 2x1 texture, ready for pre-multiplied alpha. The red pixel is (1, 0, 0, 1) the green pixel (0, 0.1, 0, 0.1)](https://i-blog.csdnimg.cn/blog_migrate/9e0daf20befe39949fe570c0f733bfcb.png)
And the 1x1 mipmap level of this texture would look like this:
![The 1x1 mipmap of our pre-multiplied alpha texture. RGBA = (0.5, 0.05, 0, 0.55)](https://i-blog.csdnimg.cn/blog_migrate/191594238ae482a40b891a8d8fdcc786.png)
This is much more reasonable. We've still lost some information (and note that if the green component were small enough, or if our precision is too low the green will drop out entirely), but we've preserved the intent of the higher resolution mipmap. For comparison, here are the images again, with the addition of our pre-multiplied blender:
![The canonical mandrill face.](https://i-blog.csdnimg.cn/blog_migrate/d4e7ea0ee10d428f47d02c111018fdc0.png)
![Our 2x1-rg image from above pre-multiplied atop the mandril's face.](https://i-blog.csdnimg.cn/blog_migrate/e7ed29f5f7953a5b11425f13666e615c.png)
![Our 2x1-rg image from above post-multiplied blend atop the mandril's face.](https://i-blog.csdnimg.cn/blog_migrate/6dc2ab4385e4b66dff9d561131208887.png)
![Our 1x1-rg image (the next coarser mipmap) pre-blended atop the mandril's face.](https://i-blog.csdnimg.cn/blog_migrate/20cb010aa4fcd6ba228e819831446285.png)
![Our 1x1-rg image (the next coarser mipmap) post-blended atop the mandril's face.](https://i-blog.csdnimg.cn/blog_migrate/d32918e111bb91ece278910ad60f545d.png)
Transitioning from our post-multiplied world
Conveniently, the conversion from a post-multiplied alpha pipeline to one utilizing pre-multiplied alpha is trivial. At texture save time, or asset baking time, or even load time, multiply each non-alpha channel by alpha. That is:
OutputTexture.rgb = InputTexture.rgb * InputTexture.a; OutputTexture.a = InputTexture.a;
And don't forget to modify your "alpha blending enable" to use One for the Source Alpha value. If we plug in pre-multiplied alpha to the original blending equation, it's easy to see that switching to pre-multiplied alpha gives us the exact same result:
DestinationColor.rgb = ((SourceColor.rgb * SourceColor.a) * One) + (DestinationColor.rgb * (1 - SourceColor.a));
So you might be asking: If the result is the same, why bother with pre-multiplied alpha? The reason is texture filtering. When you take samples from a texture, unless you have disabled texture filtering, the hardware is blending neighboring texels together and returning a weighted average as a result. With traditional post-multiplied alpha, this result will be incorrect. If your browser supports WebGL, you can play with pre- and post- multiplicative blending below: