I've heard that pre-multiplied alpha gives you order independent transparency but when I sit down and do the math, it doesn't seem to be working.
Is that untrue, or am I doing something incorrectly?
The formula I'm using is:
$out_{rgba} = in_{rgba} + out_{rgba} * (1 - in_a)$
where $in$ is premultiplied alpha. In other words, taking a "normal" color in RGBA, i multiply RGB by a. 30% opaque white would start out as (1, 1, 1, 0.3) but would become (0.3, 0.3, 0.3, 0.3) as premultiplied alpha.
After getting the wrong answers when working it out by hand, I wrote the C++ program below and am still getting the wrong results.
After execution:
$out1 = (0.738, 0.913, 0.3, 1.0)\\ out2 = (0.738, 0.875, 0.113, 1.0)$
Can anyone explain why?
#include <array>
typedef std::array<float, 4> RGBA;
void PremultiplyAlpha (RGBA& rgba)
{
rgba[0] *= rgba[3];
rgba[1] *= rgba[3];
rgba[2] *= rgba[3];
}
RGBA BlendPremultipliedAlpha (const RGBA& dest, const RGBA& src)
{
RGBA ret;
ret[0] = src[0] + dest[0] * (1.0f - src[3]);
ret[1] = src[1] + dest[1] * (1.0f - src[3]);
ret[2] = src[2] + dest[2] * (1.0f - src[3]);
ret[3] = src[3] + dest[3] * (1.0f - src[3]);
return ret;
}
int main(int argc, char **argv)
{
RGBA greenGround = { 0.0f, 1.0f, 0.0f, 1.0f };
PremultiplyAlpha(greenGround);
RGBA red25PercentOpaque = { 1.0f, 0.0f, 0.0f, 0.25f };
PremultiplyAlpha(red25PercentOpaque);
RGBA white30PercentOpaque = { 1.0f, 1.0f, 1.0f, 0.3f };
PremultiplyAlpha(white30PercentOpaque);
RGBA yellow50PercentOpaque = { 1.0f, 1.0f, 0.0f, 0.5f };
PremultiplyAlpha(yellow50PercentOpaque);
// one way
RGBA out1;
{
// start with the green ground and blend in 25% opaque red
out1 = greenGround;
out1 = BlendPremultipliedAlpha(out1, red25PercentOpaque);
// then blend in 50% yellow
out1 = BlendPremultipliedAlpha(out1, yellow50PercentOpaque);
// then blend in 30% opaque white
out1 = BlendPremultipliedAlpha(out1, white30PercentOpaque);
}
// other way
RGBA out2;
{
// start with the green ground and blend in 30% opaque white
out2 = greenGround;
out2 = BlendPremultipliedAlpha(out2, white30PercentOpaque);
// then blend in 25% red
out2 = BlendPremultipliedAlpha(out2, red25PercentOpaque);
// then blend in 50% yellow
out2 = BlendPremultipliedAlpha(out2, yellow50PercentOpaque);
}
return 0;
}