-
Notifications
You must be signed in to change notification settings - Fork 0
Compositing
Compositing is a fundamental technique in image synthesis. Conceptually, every write to a buffer that is interpreted as an image can fall in this category. I can highly recommend reading the paper "Compositing Digital Images" by Thomas Porter and Tom Duff, some of the OGs of computer graphics.
In OpenGL terminology "blending" means that the output of the fragment shader also depends on the currently stored fragment. This behavior can be enabled by calling gl.enable(gl.BLEND)
. There are multiple options for the blending functionalities. When using blending with gl.blendFunc(sfactor, dfactor)
the output will be calculated as:
-
$\mathbf{O}$ the output color vector -
$\mathbf{S}$ the source vector - the output color of the fragment shader -
$\mathbf{D}$ the destination vector - the currently stored color -
$\mathbf{S}_f$ the source factor vector -
$\mathbf{D}_f$ the destination factor vector -
$\circ$ the hadamarad product operator for vectors -
$\mathbf{S}_f$ and$\mathbf{D}_f$ are one of:
Constant | Factor RGB | Factor A |
---|---|---|
gl.ZERO |
||
gl.ONE |
||
gl.SRC_COLOR |
||
gl.ONE_MINUS_SRC_COLOR |
||
gl.DST_COLOR |
||
gl.ONE_MINUS_DST_COLOR |
||
gl.SRC_ALPHA |
||
gl.ONE_MINUS_SRC_ALPHA |
||
gl.DST_ALPHA |
||
gl.ONE_MINUS_DST_ALPHA |
||
gl.CONSTANT_COLOR |
||
gl.ONE_MINUS_CONST_COLOR |
||
gl.CONTANT_ALPHA |
||
gl.ONE_MINUS_CONSTANT_ALPHA |
||
gl.SRC_ALPHA_SATURATE |
The CONSTANT_COLOR
can be set with gl.blendColor(r, g, b, a)
. The operation by which the source and destination are combined does not have to be addition. By using gl.blendEquation(mode)
it can be set to one of
mode | function |
---|---|
gl.FUNC_ADD |
|
gl.FUNC_SUBTRACT |
|
gl.FUNC_REVERSE_SUBTRACT |
|
gl.MIN |
|
gl.MAX |
where
gl.FUNC_ADD
, gl.FUNC_SUBTRACT
andgl.FUNC_REVERSE_SUBTRACT
and to
gl.MIN
and gl.MAX
.
OpenGL and WebGL also provide the option to set the factors and function separately for the alpha component by using blendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha)
and blendEquationSeparate(modeRGB, modeAlpha)
respectively. This extends the equation to
or a respective form for gl.MIN
and gl.MAX
without the factor. This leaves us with
The blend over operation is the "natural" operation when layering one transparent image over another. There are a thew cases to consider with the blend over operation.
Color Model | RGB | A |
---|---|---|
straight alpha | the albedo of the pixel | the opacity of this layer |
premultiplied alpha | the emission of the pixel | the occlusion of the layer beneath |
While premultiplied alpha is less intuitive, it has some major advantages, as well mathematically and conceptionally, as we will see in the next sections.
With straight alpha the RGB components represent the albedo of the surface and the alpha component represents the opacity of the surface. That is a relatively intuitive representation as it resembles the idea of thin colored plastic sheets.
If the alpha of the destination is known to be
gl.blendEquation(gl.FUNC_ADD);
gl.blendFuncSeperate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ZERO, gl.ONE);
If the alpha of the destination is not
which yields:
This cannot be translated to a GL blend function, but must be implemented manually using a shader and a third buffer.
With premultiplied alpha the RGB components represent the emission of the surface and the alpha component represents the occlusion of the layer beneath. This can be considered less intuitive as there is no haptic concept in the physical world resembling these properties. However when considering the blend over function you start seeing the advantages of premultiplied alpha. To convert from a straight alpha color to a premultiplied alpha color, the RGB values are multiplied by the alpha:
Applying this to the RGB equation for the blend over model, we get:
which simplifies to:
The equation for the blended alpha component is the same for premultiplied and straight alpha. Since the form for both RGB and alpha are the same for premultiplied alpha, both can be described using one equation:
which translates to:
gl.blendEquation(gl.FUNC_ADD);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
This is the Footer. Nothing to interesting yet.