Blending transparent colors (multiple layers)

This topic is 2451 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

Recommended Posts

I'll start with an introduction of the problem then explain the math question at the bottom. This should be a simple problem, but I'm drawing a blank.

Okay the most basic blending equation for multiple layers handling things front to back is:
colorR += alpha * red * (1 - colorA);
colorG += alpha * green * (1 - colorA);
colorB += alpha * blue * (1 - colorA);
colorA += alpha * (1 - colorA);

Essentially you start with (0, 0, 0, 0) then for each color (red, green, blue, alpha) you multiply the alpha then add it to accumulator variables weighting it by 1 - colorA which represents the last of the "alpha" to be used before the color becomes opaque. At the end of adding all the layers you'd simply divide each channel by the colorA accumulator to get the final pixel color.

Okay there is a slight flaw with this blending equation though. Lets say for simplicity we're blending two black colors with alpha 0.5 and 0.2. Since red, green, and blue are all 0 for the color we don't have to compute and add anything to the colorR, colorG, and colorB accumulators. All we need to calculate is the colorA one.

colorA += alpha * (1 - colorA);

Let's assume that the colorA accumulator starts with 0.1. (For instance if this wasn't the first layer).

// Start colorA then add two colors, one with 0.5 alpha for the first and 0.2 for the second
colorA = 0.1;
0.1 += 0.5 * (1 - 0.1);
0.55 += 0.2 * (1 - 0.55);
0.64

So we ended up with 0.64 alpha.

0.2 and 0.5 should be equivalent to a single layer with 0.7 alpha. However if we do this computation we notice we get a different result.
0.1 += (0.5 + 0.2) * (1 - 0.1)
0.73

One way I thought to make sure we get the same result is to break up the addition (this equation is a recurrence relation basically) into tons of pieces. Lets say we performed the computation with steps of 0.1 for the 0.5 and 0.2 layers:
0.1 += 0.1 * (1 - 0.1);
0.19 += 0.1 * (1 - 0.19);
0.271 += 0.1 * (1 - 0.271);
0.3439 += 0.1 * (1 - 0.3439);
0.40951 += 0.1 * (1 - 0.40951);
0.468559
// Now we'll add the 0.2
0.468559 += 0.1 * (1 - 0.468559)
0.5217031 += 0.1 * (1 - 0.5217031)
0.56953279

If we did the same computation with 0.7 we'd get the same result! But that doesn't work for numbers like colorA = 0 then a single layer with alpha = 1. We'd end up with a number less than 1.

Okay what I'm looking for is an equation that assumes infinitely small "steps" and would thus give the same result for any number of layers of the same color treated as one layer. So if we had 3 layers of black with 0.2, 0.3, and 0.4 alpha it would give the same result as one layer with 0.9 alpha.

Share on other sites
Hidden
Okay a night's rest seems to have kicked my brain into gear.

I take my equation:
colorA += alpha * (1 - colorA);
Rearrange it into:
colorA = colorA + alpha * (1 - colorA); // Turn += into =
colorA = colorA + alpha - alpha * colorA; // Distribute
colorA = alpha + colorA + colorA * -alpha; // Rearrange
colorA = alpha + colorA(1 - alpha); // Pull out colorA

Now it's a simple recurrence relation! Seen easier by writing it as:
f(n + 1) = alpha + f(n) * (1 - alpha)
Then since solving recurrence relations is the work of the devil:
f(n) = (a + c - 1)(1 - a)^(n-1) + 1
That is the solution for any value of n where c is the first initial value.

Now we take the limit as n goes to infinity and break a into infinitesimal parts:
lim((a / n + c - 1)(1 - a / n)^(n - 1) + 1, {n, 0, infinity})
And by the power of black magic:
(-1 + c + e^a) / e^a
Rewritten with colorA we get:
colorA = (-1 + colorA + e^a) / e^a

We can test this by using:
Two black layers, one with 0.5 alpha and the other with 0.2 alpha with c = 0.1
(-1 + 0.1 + e^0.5)/(e^0.5) = 0.454122406
(-1 + 0.454122406 + e^0.2)/(e^0.2) = 0.553073226

Now we compare that to 0.5 + 0.2 = 0.7:
(-1 + 0.1 + e^0.7)/(e^0.7) = 0.553073226

My math background has payed off. Be cool, stay in school.

IMHO you can't sum up the colorA simply the way you do it.

Well, the standard blending function looks like
 alpha * X + (1-alpha)*Y 

When applying the blending calculation for multiple layers you would get something like this:
 Layers: X,Y,Z,W a = alpha b = beta g = gamma // blend X & Y aX + (1-a)Y // blend X,Y,Z aX + (1-a)(bY+(1-b)Z) <=> aX + (1-a)bY + (1-a)(1-b)Z // blend X,Y,Z,W aX + (1-a)bY + (1-a)(1-b) (gZ+(1-g)W) <=> aX + (1-a)bY + (1-a)(1-b)gZ + (1-a)(1-b)(1-g)W Break the last function up: result = aX alpha = (1-a) result+=alpha * bY alpha *= (1-b) result+=alpha * gZ alpha *= (1-g) result+=alpha * 1W alpha *= (1-1) 
As you can see, you need to multiply the "alpha" values instead of summing up. The last iteration has an alpha value of 1 (solid blend).