Compositor         The Compositor Framework

The Compositor Framework

Here we will describe the Ogre in-core compositing system in Dagon. It is very roughly based on Manuel's postfilter framework introduced here. By using this system, you'll be able to implement postfilter effects in Ogre just like you think about compositing the passes.

  • PostFilters have from hereon been renamed to Compositors. Seeing the system grow into much more than just simple postfiltering, I(Wumpus) decided on this renaming.

Compositor scripts

Compositors can be built from code or by writing .compositor scripts. Here are some examples.

Bloom:

/// Manuel's bloom
/// Needs a scene-sized rtt, but does only one render of the scene
compositor Bloom
{
    technique
    {
        // Temporary textures
        texture scene target_width target_height PF_A8R8G8B8
        texture rt0 128 128 PF_A8R8G8B8
        texture rt1 128 128 PF_A8R8G8B8
    
        target scene
        {
            // Render output from previous compositor (or original scene)
            input previous
        }
        target rt0
        {
            // Start with clear texture
            input none
            // Vertical blur pass
            pass render_quad
            {
                // Renders a fullscreen quad with a material
                material PostFilters/Blur0
                input 0 scene
            }
        }
        target rt1
        {
            // Start with clear texture
            input none
            // Horizontal blur pass
            pass render_quad
            {
                // Renders a fullscreen quad with a material
                material PostFilters/Blur1
                input 0 rt0
            }
        }
        target_output
        {
            // Start with clear output
            input none
            // Draw a fullscreen quad
            pass render_quad
            {
                // Renders a fullscreen quad with a material
                material PostFilters/BloomBlend
                input 0 scene
                input 1 rt1
            }
        }
    }
}

Alternative bloom

Suggestion by Sinbad; this needs no screen sized RenderTexture and can selectively make object glare (but does render the scene twice)

/// Alternative bloom by Sinbad
/// Needs only two 128x128 rtts, but does two renders of the scene
compositor Bloom2
{
    technique
    {
        // Temporary textures
        texture rt0 128 128 PF_A8R8G8B8
        texture rt1 128 128 PF_A8R8G8B8
    
        target rt1
        {
            // Render output from previous compositor (or original scene)
            input previous
        }
        target rt0
        {
            // Start with clear texture
            input none
            // Vertical blur pass
            pass render_quad
            {
                // Renders a fullscreen quad with a material
                material PostFilters/Blur0
                input 0 rt1
            }
        }
        target rt1
        {
            // Start with clear texture
            input none
            // Horizontal blur pass
            pass render_quad
            {
                // Renders a fullscreen quad with a material
                material PostFilters/Blur1
                input 0 rt0
            }
        }
        target_output
        {
            // Render output from previous pass (or original scene)
            input previous
            // Blend a fullscreen quad over the scene
            pass render_quad
            {
                // Renders a fullscreen quad with a material
                material PostFilters/BloomBlend
                input 0 rt1
            }
        }
    }
}

Motion Blur

/// Manuel's motion blur effect
compositor "Motion Blur"
{
    technique
    {
        // Temporary textures
        texture scene target_width target_height PF_A8R8G8B8
        texture sum target_width target_height PF_A8R8G8B8
        texture temp target_width target_height PF_A8R8G8B8
        
        target scene
        {
            // Render output from previous compositor (or original scene)
            input previous
        }
        target sum
        {
            // Render this target pass only initially, on the first frame that
            // this effect was enabled. This makes sure the sum texture
            // does not start with randomness.
            only_initial on
            
            input previous
        }
        target temp
        {
            // Start with clear texture
            input none
            // Combine scene and sum texture
            pass render_quad
            {
                // Renders a fullscreen quad with a material
                material Ogre/Compositors/Combine
                input 0 scene
                input 1 sum
            }
        }
        target sum
        {
            // Start with clear texture
            input none
            // Copy temporary back to sum
            pass render_quad
            {
                // Renders a fullscreen quad with a material
                material Ogre/Compositors/Copyback
                input 0 temp
            }
        }
        target_output
        {
            // Start with clear output
            input none
            // Draw a fullscreen quad with the blur
            pass render_quad
            {
                // Renders a fullscreen quad with a material
                material Ogre/Compositors/MotionBlur
                input 0 sum
            }
        }
    }
}

Black & White

/// Black and white effect
compositor B&W
{
    technique
    {
        // Temporary textures
        texture scene target_width target_height PF_A8R8G8B8
    
        target scene
        {
            // Render output from previous compositor (or original scene)
            input previous
        }
        target_output
        {
            // Start with clear output
            input none
            // Draw a fullscreen quad with the blur
            pass render_quad
            {
                // Renders a fullscreen quad with a material
                material PostFilters/BlackAndWhite
                input 0 scene
            }
        }
    }
}

Chaining postfilters

Chaining postfilters is as easy as adding multiple Compositors to one render target,

CompositorManager::getSingleton().addCompositor(mViewport, "Blur");
CompositorManager::getSingleton().addCompositor(mViewport, "B&W");

After adding them, compositors can be enabled and disabled individually, or removed again with removeCompositor. In case a postfilter is enabled and disabled frequently, one should use the enableCompositor and disableCompositor functionality as that keeps local resources like scratch textures around.

Script Manual

Here is the Compositor script manual released with Ogre 1.6

BNF grammer for script

This is the BNF grammer for the compositor script utilizing the Ogre simplified BNF compiler

// Top level rule
<Script> ::= {<Compositor>}
<Compositor> ::= 'compositor' <Label> '{' <Technique> '}'

// Technique
<Technique> ::= 'technique' '{' {<Texture>} {<Target>} <TargetOutput> '}'
<Texture> ::= 'texture' <Label> <WidthOption> <HeightOption> <PixelFormat>
<WidthOption> ::= 'target_width' | <#width>
<HeightOption> ::= 'target_height' | <#height>
<PixelFormat> ::= 'PF_A8R8G8B8' | 'PF_R8G8B8A8' | 'PF_R8G8B8' | 'PF_FLOAT16_RGBA' |
  'PF_FLOAT16_RGB' | 'PF_FLOAT16_R' | 'PF_FLOAT32_RGBA' | 'PF_FLOAT32_RGB' | 'PF_FLOAT32_R'

// Target
<Target> ::= 'target ' <Label> '{' {<TargetOptions>} {<Pass>} '}'
  <TargetOptions> ::=    <TargetInput> | <OnlyInitial> | <VisibilityMask> |
    <LodBias> | <MaterialScheme>
<TargetInput> ::= 'input' <TargetInputOptions>
<TargetInputOptions> ::= 'none' | 'previous'
<OnlyInitial> ::= 'only_initial' <On_Off>
<VisibilityMask> ::= 'visibility_mask' <#mask>
<LodBias> ::= 'lod_bias' <#lodbias>
<MaterialScheme> ::= 'material_scheme' <Label>
<TargetOutput> ::= 'target_output' '{' [<TargetInput>] {<Pass>} '}'

// Pass
<Pass> ::= 'pass' <PassTypes> '{' {<PassOptions>} '}'
<PassTypes> ::= 'render_quad' | 'clear' | 'stencil' | 'render_scene'
<PassOptions> ::= <PassMaterial> | <PassInput> | <ClearSection> | <StencilSection>
<PassMaterial> ::= 'material' <Label>
<PassInput> ::= 'input' <#id> <Label>
// clear
<ClearSection> ::= -'clear' -'{' {<ClearOptions>} -'}'
<ClearOptions> ::= <Buffers> | <ColourValue> | <DepthValue> | <StencilValue>
<Buffers> ::= 'buffers' {<BufferTypes>}
<BufferTypes> ::= <Colour> | <Depth> | <Stencil>
<Colour> ::= 'colour' (?!<ValueChk>)
<Depth> ::= 'depth' (?!<ValueChk>)
<Stencil> ::= 'stencil' (?!<ValueChk>)
<ValueChk> ::= '_value'
<ColourValue> ::= 'colour_value' <#red> <#green> <#blue> <#alpha>
<DepthValue> ::= 'depth_value' <#depth>
<StencilValue> ::= 'stencil_value' <#val>
// stencil
<StencilSection> ::= -'stencil' -'{' {<StencilOptions>} -'}'
<StencilOptions> ::=  <Check> | <CompareFunction> | <RefVal> | <Mask> | <FailOp> | <DepthFailOp> |
   <PassOp> | <TwoSided>
<Check> ::= 'check' <On_Off>
<CompareFunction> ::= 'comp_func' <CompFunc>
<CompFunc> ::= 'always_fail' | 'always_pass' | 'less_equal' | 'less' | 'equal' |
   'not_equal' | 'equal' | 'greater_equal' | 'greater'
<RefVal> ::= 'ref_value' <#val>
<Mask> ::= 'mask' <#mask>
<FailOp> ::= 'fail_op' <StencilOperation>
<DepthFailOp> ::= 'depth_fail_op' <StencilOperation>
<PassOp> ::= 'pass_op' <StencilOperation>
<TwoSided> ::= 'two_sided' <On_Off>
<StencilOperation> ::= 'keep' | 'zero' | 'replace' | 'increment_wrap' | 'increment' |
   'decrement_wrap' | 'decrement' | 'invert'

// common rules
<On_Off> ::= 'on' | 'off'
<Label> ::= <Unquoted_Label> | <Quoted_Label>
<Quoted_Label> ::= -'\"' <Character> {<Alphanumeric_Space>} -'\"' 
<Unquoted_Label> ::= <Character> {<Alphanumeric>}
<Alphanumeric_Space> ::= <Alphanumeric> | <Space>
<Alphanumeric> ::= <Character> | <Number>
<Character> ::= (abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$#%!_*&\\/)
<Number> ::= (0123456789)
<Space> ::= ( )

Modifying a compositor material

Each compositor instance clones the original material for the compositor, so they can be modified independently for each viewport where the compositor is used. The drawback is that changes to the original material aren't transmitted into the cloned versions.

Thus, modifying the material used by a compositor instance requires a CompositorInstance::Listener specialization. This listener has two variation points:

  • notifyMaterialSetup(): Called when the material is initialised/compiled.
  • notifyMaterialRender(): Called when the material will be used by the render target.




The materials passed as argument to these methods are the clones used by the compositor instances. Changes done to them will take effect in each compositor referencing this listener.

The way to add a listener to a compositor instance is:

aCompositorInstance->addListener (new MyCompositorListener);

Compositors and camera switching

If you're using compositors and switching between different cameras regularly, you might notice that the other cameras stop working and only the camera that was active when you added the compositors is used - this is because the render targets' reference to the camera are not updated when you change the viewport's camera. To fix this, just disable all compositors before switching the camera and enable them again afterwards. This destroys and recreates the render targets, which is a bit inefficient but it's a good enough workaround.

See this thread for more information: http://www.ogre3d.org/forums/viewtopic.php?f=4&t=53330

This has been fixed in a patch for Ogre 1.7, so it only affects older versions and MOGRE.