The main problem with alpha blending formula is how source/destination/result colors are stored. We have 2 cases: alpha-premultiplied (r,g,b channels are multiplied by alpha channel); not-alpha-premultiplied (r,g,b are not multiplied). You have to ensure which formats are used to use correct blending formulas.
alpha-premultiplication is often used when rendering something to the screen, it allows to use simpler and faster blending formula.
I gathered all basic cases below, but first some informations:
- what is source/destination: if you think in "Photoshop" way you can treat destination as bottom layer, and source and top layer.
- mix functions: it is shader linear interpolation function (also know as lerp). From OpenGL reference: mix performs a linear interpolation between X and Y using A to weight between them:
mix(x, y, a) = x * (1 - a) + y * a
. From my experience i can say mix
version is a bit faster than standard formula when using it in gpu fragment shaders.
- all formulas are for float color values (from 0.0 to 1.0)
- if you want to use opacity for source color you have to just multiply src.a by the opacity
src.a *= opacity
- in formulas where division by 0 can happen you probably have to add an "if" condition like this:
result.a = <formula for alpha>;
if (result.a == 0.0) {
result.rgb = dest.rgb;
} else {
result.rgb = <formula for rgb>;
}
So here are the formulas:
1. Source, Destination, Result are not premultiplied
result.a = dest.a * (1.0 - src.a) + src.a;
result.rgb = (dest.rgb * dest.a * (1.0 - src.a) + src.rgb * src.a) / result.a; // division by 0
or using mix function:
result.a = mix(dest.rgb, 1.0, src.a);
result.rgb = mix(dest.rgb * dest.a, src.rgb, src.a) / result.a; // division by 0!!!!
OpenGL blend functions:
not possible
2. Source, Destination, Result are all premultiplied
result.a = dest.a * (1.0 - src.a) + src.a;
result.rgb = dest.rgb * (1.0 - src.a) + src.rgb;
mix version:
result.a = mix(dest.rgb, 1.0, src.a);
result.rgb = dest.rgb * (1.0 - src.a) + src.rgb; // "mix" version not available
OpenGL blend functions:
glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
3. Destination, Result premultiplied, Source not premultiplied
result.a = dest.a * (1.0 - src.a) + src.a;
result.rgb = dest.rgb * (1.0 - src.a) + src.rgb * src.a;
mix version:
result.a = mix(dest.rgb, 1.0, src.a);
result.rgb = mix(dest.rgb, src.rgb, src.a);
OpenGL blend functions:
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
4. Destination is opaque (dest.a = 1.0) so Result is opaque, Source non premultiplied
result.rgb = dest.rgb * (1.0 - src.a) + src.rgb * src.a;
result.a = 1.0;
mix version:
result.rgb = mix(dest.rgb, src.rgb, src.a);
result.a = 1.0;
OpenGL blend functions:
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);