07. Blending

Without blending, every fragment is either rejected or written to the framebuffer. That behaviour is desireable for opaque objects, but it does not allow rendering of translucent objects. The correct order of operation to draw a simple scene containing a solid table with a transparent glass ontop of it: draw the opaque table first, then enable blending (also set the desired blend equation and factors) and finally the glass is drawn.

Blending is an operation to mix the incoming fragment color (SourceColor) with the color that is currently in the color buffer (DestinationColor). This happens in two stages for each channel of the color buffer:

  1. The factors used in this stage can be controlled with GL.BlendFunc()
    The SourceColor is multiplied by the SourceFactor.
    The DestinationColor is multiplied by the DestinationFactor.
  2. The equation used in this stage can be controlled with GL.BlendEquation()
    The results of the above multiplications are then combined together to obtain the final result.

In order to use blending, the logical color buffer must have an Alpha channel. If there is no Alpha channel, or the color buffer uses color-index mode (8 Bit), no blending can occur and behaviour is the same as if blending was disabled.

Blending functionality for all draw buffers is enabled and disabled with EnableCap.Blend

GL.Enable( EnableCap.Blend );
GL.Disable( EnableCap.Blend ); // default

To enable or disable only a specific buffer if multiple color buffers are attached to the FBO, use

GL.Enable( IndexedEnableCap.Blend, index);
GL.Disable( IndexedEnableCap.Blend, index);

Where index is used to specify the draw buffer associated with the symbolic constant GL_DRAW_BUFFER(index).

GL.BlendEquation
The command GL.BlendEquation( mode ) specifies how the results from stage 1 are combined with each other. If you wanted to implement this with OpenTK.Math, it would look like this:

Color4 SourceColor, // incoming fragment
       DestinationColor,  // framebuffer contents
       SourceFactor, DestinationFactor, // specified by GL.BlendFunc()
       FinalColor; // the resulting color

Please note that for fixed-point color buffers both Colors are clamped to [0.0 .. 1.0] prior to computing the result. Floating-point color buffers are not clamped. Clamping into this range is left out in this code to improve legibility.

BlendEquationMode.Min: When using this parameter, SourceFactor and DestinationFactor are ignored.

FinalColor.Red = min( SourceColor.Red, DestinationColor.Red );
FinalColor.Green = min( SourceColor.Green, DestinationColor.Green );
FinalColor.Blue = min( SourceColor.Blue, DestinationColor.Blue );
FinalColor.Alpha = min( SourceColor.Alpha, DestinationColor.Alpha );

BlendEquationMode.Max: When using this parameter, SourceFactor and DestinationFactor are ignored.

FinalColor.Red = max( SourceColor.Red, DestinationColor.Red );
FinalColor.Green = max( SourceColor.Green, DestinationColor.Green );
FinalColor.Blue = max( SourceColor.Blue, DestinationColor.Blue );
FinalColor.Alpha = max( SourceColor.Alpha, DestinationColor.Alpha );

BlendEquationMode.FuncAdd: This is the default.

FinalColor.Red = SourceColor.Red*SourceFactor.Red + DestinationColor.Red*DestinationFactor.Red;
FinalColor.Green = SourceColor.Green*SourceFactor.Green + DestinationColor.Green*DestinationFactor.Green;
FinalColor.Blue = SourceColor.Blue*SourceFactor.Blue + DestinationColor.Blue*DestinationFactor.Blue;
FinalColor.Alpha = SourceColor.Alpha*SourceFactor.Alpha + DestinationColor.Alpha*DestinationFactor.Alpha;

BlendEquationMode.FuncSubtract:

FinalColor.Red = SourceColor.Red*SourceFactor.Red - DestinationColor.Red*DestinationFactor.Red;
FinalColor.Green = SourceColor.Green*SourceFactor.Green - DestinationColor.Green*DestinationFactor.Green;
FinalColor.Blue = SourceColor.Blue*SourceFactor.Blue - DestinationColor.Blue*DestinationFactor.Blue;
FinalColor.Alpha = SourceColor.Alpha*SourceFactor.Alpha - DestinationColor.Alpha*DestinationFactor.Alpha;

BlendEquationMode.FuncReverseSubtract:

FinalColor.Red = DestinationColor.Red*DestinationFactor.Red - SourceColor.Red*SourceFactor.Red;
FinalColor.Green = DestinationColor.Green*DestinationFactor.Green - SourceColor.Green*SourceFactor.Green;
FinalColor.Blue = DestinationColor.Blue*DestinationFactor.Blue - SourceColor.Blue*SourceFactor.Blue;
FinalColor.Alpha = DestinationColor.Alpha*DestinationFactor.Alpha - SourceColor.Alpha*SourceFactor.Alpha;

If the color buffer is using fixed-point precision, the result in FinalColor is clamped to [0.0 .. 1.0] before it is passed to the next pipeline stage, no clamping occurs for floating-point color buffers.

OpenGL 2.0 and later supports separate equations for the RGB components and the Alpha component respectively. The command GL.BlendEquationSeparate( modeRGB, modeAlpha ) accepts the same parameters as GL.BlendEquation( mode ).

GL.BlendColor
The command GL.BlendColor( R, G, B, A ) is used to specify a constant color that can be used by GL.BlendFunc(). For the scope of this page it is used to define the variable Color4 ConstantColor;.

GL.BlendFunc
The command GL.BlendFunc( src, dest ) is used to select the SourceFactor (src) and DestinationFactor (dest) in the above equation. By default, SourceFactor is set to BlendingFactorSrc.One and DestinationFactor is BlendingFactorDest.Zero, which gives the same result as if blending were disabled.

  • .Zero: Color4 (0.0, 0.0, 0.0, 0.0)
  • .One: Color4 (1.0, 1.0, 1.0, 1.0)
  • .DstColor: Color4 (DestinationColor.Red, DestinationColor.Green, DestinationColor.Blue, DestinationColor.Alpha)
  • .SrcColor: Color4 (SourceColor.Red, SourceColor.Green, SourceColor.Blue, SourceColor.Alpha)
  • .OneMinusDstColor: Color4 (1.0-DestinationColor.Red, 1.0-DestinationColor.Green, 1.0-DestinationColor.Blue, 1.0-DestinationColor.Alpha)
  • .OneMinusSrcColor: Color4 (1.0-SourceColor.Red, 1.0-SourceColor.Green, 1.0-SourceColor.Blue, 1.0-SourceColor.Alpha)
  • .SrcAlpha: Color4 (SourceColor.Alpha, SourceColor.Alpha, SourceColor.Alpha, SourceColor.Alpha)
  • .OneMinusSrcAlpha: Color4 (1.0-SourceColor.Alpha, 1.0-SourceColor.Alpha, 1.0-SourceColor.Alpha, 1.0-SourceColor.Alpha)
  • .DstAlpha: Color4 (DestinationColor.Alpha, DestinationColor.Alpha, DestinationColor.Alpha, DestinationColor.Alpha)
  • .OneMinusDstAlpha: Color4 (1.0-DestinationColor.Alpha, 1.0-DestinationColor.Alpha, 1.0-DestinationColor.Alpha, 1.0-DestinationColor.Alpha)
  • .SrcAlphaSaturate: f = min( SourceColor.Alpha, 1.0-DestinationColor.Alpha );
    Color4 ( f, f, f, 1.0 )
  • .ConstantColor: Color4 ( ConstantColor.Red, ConstantColor.Green, ConstantColor.Blue, ConstantColor.Alpha )
  • .OneMinusConstantColor: Color4 ( 1.0-ConstantColor.Red, 1.0-ConstantColor.Green, 1.0-ConstantColor.Blue, 1.0-ConstantColor.Alpha )
  • .ConstantAlpha: Color4 (ConstantColor.Alpha, ConstantColor.Alpha, ConstantColor.Alpha, ConstantColor.Alpha)
  • .OneMinusConstantAlpha: Color4 (1.0-ConstantColor.Alpha, 1.0-ConstantColor.Alpha, 1.0-ConstantColor.Alpha, 1.0-ConstantColor.Alpha)

OpenTK uses the enums BlendingFactorSrc and BlendingFactorDest to narrow down your options what is a valid parameter for src and dest. Not all parameters are valid factors for both, SourceFactor and DestinationFactor. Please refer to the inline documentation for details.

OpenGL 2.0 and later supports separate factors for RGB and Alpha, for source and destination respectively. The command GL.BlendFuncSeparate( srcRGB, dstRGB, srcAlpha, dstAlpha ) accepts the same factors as GL.BlendFunc( src, dest ).

State Queries

To determine whether blending for all draw buffers is enabled or disabled, use Result = GL.IsEnabled( EnableCap.Blend );
To query blending state of a specific draw buffer: Result = GL.IsEnabled(IndexedEnableCap.Blend, index);

The selected blend factors can be queried separately for source and destination by using GL.GetInteger() with

  • GetPName.BlendSrc - set by GL.BlendFunc()
  • GetPName.BlendDst - set by GL.BlendFunc()
  • GetPName.BlendSrcRgb - set by GL.BlendFuncSeparate()
  • GetPName.BlendSrcAlpha - set by GL.BlendFuncSeparate()
  • GetPName.BlendDstRgb - set by GL.BlendFuncSeparate()
  • GetPName.BlendDstAlpha - set by GL.BlendFuncSeparate()

The selected blend equation can be queried by using GL.GetInteger() with

  • GetPName.BlendEquation - set by GL.BlendEquation()
  • GetPName.BlendEquationRgb - set by GL.BlendEquationSeparate()
  • GetPName.BlendEquationAlpha - set by GL.BlendEquationSeparate()