Continuous_blending

350 days ago by PierreBdR

Continous Alpha Blending

Discrete blending

Given a screen color S_0 and a fragment color F_1 with opacity \alpha_1, the new screen color is:

S_1 = \alpha_1 F_1 + (1-\alpha_1) S_0

If we apply again a color (F_2, \alpha_2), the new screen color is:

S_2 = \alpha_2 F_2 + (1-\alpha_2) S_1

var('alpha_1 alpha_2 F_1 F_2 S_0') 
       
(alpha_1, alpha_2, F_1, F_2, S_0)
(alpha_1, alpha_2, F_1, F_2, S_0)
S_1 = alpha_1*F_1 + (1-alpha_1)*S_0 show(S_1) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}-{\left(\alpha_{1} - 1\right)} S_{0} + F_{1} \alpha_{1}
\newcommand{\Bold}[1]{\mathbf{#1}}-{\left(\alpha_{1} - 1\right)} S_{0} + F_{1} \alpha_{1}
S_2 = alpha_2*F_2 + (1-alpha_2)*S_1 show(S_2) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}{\left(\alpha_{2} - 1\right)} {\left({\left(\alpha_{1} - 1\right)} S_{0} - F_{1} \alpha_{1}\right)} + F_{2} \alpha_{2}
\newcommand{\Bold}[1]{\mathbf{#1}}{\left(\alpha_{2} - 1\right)} {\left({\left(\alpha_{1} - 1\right)} S_{0} - F_{1} \alpha_{1}\right)} + F_{2} \alpha_{2}

In the end:

S_2 = \alpha_2 F_2 + (1-\alpha_2)\alpha_1 F_1 + (1-\alpha_1)(1-\alpha_2)S_0

We can see that we can find a color equivalent to the combination of F_1 and F_2, but its expression would be fairly complex. Instead, we can note that we can simplify the initial alpha blending by using premultiplied color and alpha value. They are denoted (F_i^*, \alpha_i^*) and are equal to:

F_i^* = \alpha_i F_i \qquad \alpha_i^* = 1-\alpha_i

Alpha blending becomes:

S_1 = F_1^* + \alpha_1^*S_0

and then:

S_2 = F_2^* + \alpha_2^*(F_1^* + \alpha_1^* S_0)

S_2 = F_2^* + \alpha_2^* F_1^* + \alpha_1^*\alpha_2^* S_0

Now, the equivalent color has a simple expression:

F_{1,2}^* = F_2^* + \alpha_2^* F_1^* \qquad \alpha_{1,2}^* = \alpha_1^* \alpha_2^*

We can push farther with a third layer:

S_3 = F_3^* + \alpha_3^*S_2

S_3 = F_3^* + \alpha_3^*F_2^* + \alpha_2^*\alpha_3^*F_1^* + \alpha_1^*\alpha_2^*\alpha_3^* S_0

So the equivalent color is now:

F_{1,2,3}^* = F_3^* + \alpha_3^*F_2^* + \alpha_2^* \alpha_3^* F_1^* \qquad \alpha_{1,2}^* = \alpha_1^* \alpha_2^*\alpha_3^*

Let's denote the color-composition operator \oplus:

(F_i^*:\alpha_i^*) \oplus (F_j^*:\alpha_j^*) = (F_j^* + \alpha_j^*F_i^*: \alpha_i^*\alpha_j^*)

We can use it this way:

S_2 = (S_0\oplus F_1^*)\oplus F_2^*

For simplicity, I will assume from now on that fragment colors are always premultiplied and screen colors are not. We can then omit the star notation:

S_2 = (S_0\oplus F_1)\oplus F_2

The \oplus operator is associative:

((F_1:\alpha_1)\oplus (F_2:\alpha_2))\oplus (F_3:\alpha_3) = (F_2 + \alpha_2 F_1 : \alpha_1 \alpha_2) \oplus (F_3:\alpha_3) = (F_3 + \alpha_3 F_2 + \alpha_3 \alpha_2 F_1 : \alpha_1 \alpha_2 \alpha_3)

(F_1:\alpha_1)\oplus ((F_2:\alpha_2) \oplus (F_3:\alpha_3)) = (F_1:\alpha_1) \oplus (F_3 + \alpha_3 F_2: \alpha_2\alpha_3) = (F_3 + \alpha_3 F_2 + \alpha_2 \alpha_3 F_1:\alpha_1 \alpha_2 \alpha_3 )

We can then introduce the \bigoplus notation for a series of combination. We see easily that:

\bigoplus_{i=1}^n (F_i:\alpha_i) = \left(\sum_{i=1}^n  F_i \prod_{j=i+1}^n \alpha_j : \prod_{i=1}^n \alpha_i\right)

var('f_i f_j a_i a_j') comb(f_i,a_i,f_j,a_j) = (f_j+a_j*f_i,a_i*a_j) show(comb) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}\left( f_{i}, a_{i}, f_{j}, a_{j} \right) \ {\mapsto} \ \left(a_{j} f_{i} + f_{j},\,a_{i} a_{j}\right)
\newcommand{\Bold}[1]{\mathbf{#1}}\left( f_{i}, a_{i}, f_{j}, a_{j} \right) \ {\mapsto} \ \left(a_{j} f_{i} + f_{j},\,a_{i} a_{j}\right)

From discrete to continous

What happens if we apply many times the same color over and over:

\bigoplus_{i=1}^n(F:\alpha) = \left(F\sum_{i=0}^{n-1} \alpha^i:\alpha^n\right)

var('F alpha n i') 
       
(F, alpha, n, i)
(F, alpha, n, i)
res = sum(alpha^i,i,0,n-1) html(r'$$ \sum_{i=0}^{n-1} \alpha^i = ' + latex(res) + ' $$') 
       
\sum_{i=0}^{n-1} \alpha^i = \frac{\alpha^{n} - 1}{\alpha - 1}
\sum_{i=0}^{n-1} \alpha^i = \frac{\alpha^{n} - 1}{\alpha - 1}

The combination is then:

\bigoplus_{i=1}^n(F:\alpha) = \left(\frac{1-\alpha^n}{1-\alpha} F : \alpha^n\right)

So let's define the function C: \Re^4\times \Re \rightarrow \Re^4 such that:

C(F:\alpha,n) = \left( \frac{1-\alpha^n}{1-\alpha} F : \alpha^n \right)

We also extend C with:

C(F:1,n) = ( nF:1)

However, note that, because (F:\alpha) are premultiplied, then if \alpha=1, then F=0. So:

C(0:1,n) = (0:1)

C(alpha,n) = (1-alpha^n)/(1-alpha) show(C) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}\left( \alpha, n \right) \ {\mapsto} \ \frac{\alpha^{n} - 1}{\alpha - 1}
\newcommand{\Bold}[1]{\mathbf{#1}}\left( \alpha, n \right) \ {\mapsto} \ \frac{\alpha^{n} - 1}{\alpha - 1}
p = plot(C(.5,n), n, 0, 3) p.axes_labels(['n', 'C']) p.show() 
       
p3d = plot3d(C(alpha,n), (alpha,0.001, .999), (n,0, 3), adaptive=True, color=rainbow(60, 'rgbtuple')) p3d.show() 
       
C0 = simplify(C(0,n)) html('$$C(0,n) = ' + latex(C0) + '$$') C1 = simplify(lim(C(alpha,n), alpha = 1, dir='-')) html(r'$$C(1,n) = \lim_{\alpha\rightarrow 1-} C(\alpha,n) = ' + latex(C1) + '$$') 
       
C(0,n) = 1
C(1,n) = \lim_{\alpha\rightarrow 1-} C(\alpha,n) = n
C(0,n) = 1
C(1,n) = \lim_{\alpha\rightarrow 1-} C(\alpha,n) = n

Checking of bounds

The function C is a function from \left[0,1\right]^4 to \left[0,1\right]^4. In other terms, the elements of a color are from 0 to 1, and so is the transparency.

First, the function \alpha \mapsto \alpha^n is from [0,1] to [0,1], whatever n is.

For the color, we can first note that, as we are working on pre-multiplied values, the upper bound is 1-\alpha. We can also note that the function is monotonic increasing with respect to the color, so we only need to evaluate the function on the bounds of the source set.

Fmax(alpha,n) = C(alpha,n)*(1-alpha) show(Fmax) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}\left( \alpha, n \right) \ {\mapsto} \ -\alpha^{n} + 1
\newcommand{\Bold}[1]{\mathbf{#1}}\left( \alpha, n \right) \ {\mapsto} \ -\alpha^{n} + 1

It is obvious that 1-\alpha^n is between 0 and 1. Also, C(0:\alpha,n) = (0:\alpha^n).

Therefore the color is always between 0 and 1.