Documentation/Programming Tutorials/Creation of a new Posteffect

From NeoAxis 3D Engine Wiki

Jump to: navigation, search
Go to higher level
Posteffect Glass

Contents

Introduction

Creating posteffects involves processing an already rendered frame of a scene using shaders. A typical example of postprocessing is the image blur by high-speeed camera movement (Motion Blur) or the simulation of an infrared imager (Heat Vision).

In this tutorial you will learn how to create and use your own posteffects.

You can find the standard posteffects of NeoAxis Engine in the "Game\Bin\Data\Materials\PostEffects" folder. To see a posteffect “in action” call the menu by pressing Escape and select the Post effects option from the list.

Posteffect2.jpg

In the posteffects menu you can switch the posteffects you need on and off as well as adjust their parameters.

Adjusting the HDR posteffect

Creating a posteffect

There are 4 types of files that have to do with posteffects:

  • Files containing posteffects (.compositor),
  • Files containing materials (.material),
  • Shaders (.hlsl),
  • Textures (.dds).

A posteffect file controls image processing and the additional textures it creates. Materials process images using shaders. It is also possible to fuse previously created textures with those being processed.

As an example you can create a posteffect that implements color correction of an already rendered scene (i.e. multiplying the color of each pixel by a certain value).

First, create the ColorCorrection folder in the "Game\Bin\Data\Materials\PostEffects" directory.

Hint: You do not always need to start new post effect creation from a scratch. It is always possible to copy and edit files of a similar posteffect that already exists.

Writing a new posteffect

Add the ColorCorrection.compositor file to the folder you have just created. This will be the main file to store the posteffect data. Add the following code to this file:

compositor ColorCorrection
{
	technique
	{
		// Temporary textures
		texture rt0 target_width target_height PF_A8R8G8B8
 
		target rt0
		{
			// Render output from previous compositor (or original scene)
			input previous
		}
 
		target_output
		{
			// Start with clear output
			input none
 
			// Draw a fullscreen quad with the colorscale image
			pass render_quad
			{
				// Renders a fullscreen quad with a material
				material Compositor/ColorCorrection
				input 0 rt0
			}
		}
	}
}

Pay special attention to the render_quad block. Its output is a texture processed with the use of the ColorCorrection material. This script requires the rt0 extra texture to run.

You can find the detailed description of the .compositor type files in the official manual for the OGRE engine.

Creating a new material

Create the ColorCorrection.material file in your posteffect folder. Add the following script to it:

fragment_program Compositor/ColorCorrection_fp_hlsl hlsl
{
	source Materials\PostEffects\ColorCorrection\ColorCorrection.cg_hlsl
	entry_point main_fp
	target ps_2_0	
}
 
fragment_program Compositor/ColorCorrection_fp_cg cg
{
	source Materials\PostEffects\ColorCorrection\ColorCorrection.cg_hlsl
	entry_point main_fp
	profiles arbfp1
}
 
fragment_program Compositor/ColorCorrection_fp unified
{
	delegate Compositor/ColorCorrection_fp_hlsl
	delegate Compositor/ColorCorrection_fp_cg
}
 
material Compositor/ColorCorrection
{
	technique
	{
 
		pass
		{
			depth_check off
 
			vertex_program_ref Compositor/StdQuad_vp
			{
			}
 
			fragment_program_ref Compositor/ColorCorrection_fp
			{
			}
 
			texture_unit
			{
				tex_address_mode clamp
				filtering none
			}
		}
	}
}

Material scripts process textures (in this case those of an already rendered scene) using shaders.

Writing a new shader

Finally, the last file you need for your new posteffect is the ColorCorrection.cg_hlsl shader file. It should contain the following code:

sampler RT : register(s0);
 
float4 main_fp(
	uniform float4 multiplier,
	float2 texCoord : TEXCOORD0
) : COLOR
{
	multiplier = float4(1.5, 1, 1, 1);
 
	return tex2D(RT, texCoord) * multiplier;
}

The following shader multiplies the color value of each pixel of the texture by the multiplier parameter. Actually, this parameter is usually passed from the application, but to make things simpler this time it will be determined in the shader script. Thus, the multiplier value will be permanent multiplying red color component by 1,5.

Adding the new posteffect to the game menu

After having created all the files for the new posteffect, you have to make it accessible through the game application. To do this, start the Game project and open the PostEffectWindow.cs file. There you have to find the OnAttach method and add the following element to the listBox:

protected override void OnAttach()
{
	base.OnAttach();
 
	...
 
	listBox = (EListBox)window.Controls[ "List" ];
	listBox.Items.Add( "HDR" );
	listBox.Items.Add( "LDRBloom" );
	listBox.Items.Add( "Glass" );
	listBox.Items.Add( "OldTV" );
	listBox.Items.Add( "HeatVision" );
	listBox.Items.Add( "MotionBlur" );
	listBox.Items.Add( "RadialBlur" );
	listBox.Items.Add( "Blur" );
	listBox.Items.Add( "Grayscale" );
	listBox.Items.Add( "Invert" );
	listBox.Items.Add( "Tiling" );
 
	//Add your posteffect here...
	listBox.Items.Add("ColorCorrection");
 
	...
}

Now compile and start the project. Press the Run Demo button. To activate the new post effect you have to open the game menu (Escape) and press the Post Effects button. Now, select the Color Correction check box.

Меню постэффекта

Exit the menu and make sure that that the scene image has a reddish shade.

Постэффект, осуществляющий цветокоррекцию

Parameter transmission

Now, further improve your new posteffect by establishing parameter transmission from the application to the shader. While before you had to multiply each pixel of the scene by a certain permanent value, now you will be able to adjust color correction from the menu.

Changing posteffect script

Although the following changes would be of no significance for the posteffect you have created, they can be of use with more complex posteffects. While rendering a quad (render_quad) in a posteffect you can generate a name for it. It is required to distinguish quads in the posteffect class method. With this method it may be required to transmit parameters to the shader (just as you need it right now). However, if the posteffect is using several quads they have to be distinguished, and thus each is given a name. In this case you have only one single quad, but we recommend that you assign a name to it for the purpose of training. You can do it using the following string: identifier 500.

500 is an integral quad identifier that you can choose at will. The identifier has to be unique for the given posteffect.

Here is the full printout of the posteffect script:

compositor ColorCorrection
{
	technique
	{
		// Temporary textures
		texture rt0 target_width target_height PF_A8R8G8B8
 
		target rt0
		{
			// Render output from previous compositor (or original scene)
			input previous
		}
 
		target_output
		{
			// Start with clear output
			input none
 
			// Draw a fullscreen quad with the grayscale image
			pass render_quad
			{
				// Renders a fullscreen quad with a material
				material Compositor/ColorCorrection
				input 0 rt0
				identifier 500
			}
		}
	}
}

Changing shader code

Open the ColorCorrection.cg_hlsl shader file. Now, delete the string setting the permanent value of the multiplier parameter:

sampler RT : register(s0);
 
float4 main_fp(
	uniform float4 multiplier,
	float2 texCoord : TEXCOORD0
) : COLOR
{
	return tex2D(RT, texCoord) * multiplier;
}

Posteffect class

Now you have to make the application transmit the value of the multiplier parameter to the shader. To do so you will need to create a posteffect class. Open the GameCommon project and create the ColorCorrectionCompositorInstance.cs file in the Post Processing folder.

You can find other posteffect classes in the Post Processing folder of the GameCommon assembly and use them as an example. Let us now make the color correction affect all the color components: Red, Green and Blue. Thus, your new class has to:

  • contain multiplier values for all three color components,
  • transmit the parameter value to the shader.

You can see the code for this class below:

using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using Engine.MathEx;
using Engine.Renderer;
 
namespace GameCommon
{
    /// <summary>
    /// Color correction scene post processing compositor instance.
    /// </summary>
    [CompositorName("ColorCorrection")]
    public class ColorCorrectionCompositorInstance : CompositorInstance
    {
        static float red = 1;
        static float green = 1;
        static float blue = 1;
 
        public static float Red
        {
            get { return red; }
            set { red = value; }
        }
 
        public static float Green
        {
            get { return green; }
            set { green = value; }
        }
 
        public static float Blue
        {
            get { return blue; }
            set { blue = value; }
        }
 
        //
 
        protected override void OnMaterialRender(uint passId, Material material, ref bool skipPass)
        {
            if (passId == 500)
            {
                Vec4 multiplier = new Vec4(Red, Green, Blue, 1);
 
                GpuProgramParameters parameters = material.GetBestTechnique().
                    Passes[0].FragmentProgramParameters;
                parameters.SetNamedConstant("multiplier", multiplier);
            }
        }
    }
}

As you can see from the code, the class has three properties corresponding to the three main color components, as well as it has the OnMaterialRender method called during posteefect rendering.

In this code the identifier of the quad being rendered is checked and a single parameter of the Vec4 (a four-dimensional vector) type is created from the three color components (the fourth component, by which the shader multiplies the alpha-channel in this case will always be equal to 1. The parameter value is transmitted by means of the SetNamedConstant method of the GpuProgramParameters class.

Adding posteffect options to the menu

Finally, You have to add an element to the menu that would enable color correction adjustment. While before you could only switch the posteffect on and off, now you will be able to control it from application.

Open the PostEffectsWindow.cs file in the Game project. Now, find the UpdateCurrentPostEffectControls method. It contains the data for the posteffect menu appearance. Add your new posteffect code to the last line of the method.

void UpdateCurrentPostEffectControls()
{
    noPostEffectUpdate = true;
 
    ...
 
    //ColorCorrection specific
    if (name == "ColorCorrection")
    {
        window.Controls["FloatParameter0Text"].Visible = true;
        window.Controls["FloatParameter0Text"].Text = "Red";
        window.Controls["FloatParameter0Value"].Visible = true;
        scrollBarFloatParameters[0].Visible = true;
        scrollBarFloatParameters[0].ValueRange = new Range(0, 5);
        scrollBarFloatParameters[0].Value = ColorCorrectionCompositorInstance.Red;
 
        window.Controls["FloatParameter1Text"].Visible = true;
        window.Controls["FloatParameter1Text"].Text = "Green";
        window.Controls["FloatParameter1Value"].Visible = true;
        scrollBarFloatParameters[1].Visible = true;
        scrollBarFloatParameters[1].ValueRange = new Range(0, 5);
        scrollBarFloatParameters[1].Value = ColorCorrectionCompositorInstance.Green;
 
        window.Controls["FloatParameter2Text"].Visible = true;
        window.Controls["FloatParameter2Text"].Text = "Blue";
        window.Controls["FloatParameter2Value"].Visible = true;
        scrollBarFloatParameters[2].Visible = true;
        scrollBarFloatParameters[2].ValueRange = new Range(0, 5);
        scrollBarFloatParameters[2].Value = ColorCorrectionCompositorInstance.Blue;
    }
 
    noPostEffectUpdate = false;
}

By means of the code above you will add a UI enabling the adjustment of the three parameters of the new posteffect. You will have to create three UI elements for each property:

  • A textbox with the parameter name,
  • A slider bar to change the parameter value,
  • A textbox with the parameter value.

After having finished the adjustment of the UI appearance you will only need to make slider movement change the properties’ value of the posteffect class. To do this, find the UpdateCurrentPostEffect method in the PostEffectsWindow.cs file. There you will be able to link the sliders’ value with the value of the posteffect class properties.

Add the following code to the last line of the method:

void UpdateCurrentPostEffect()
{
    ...
 
    if( enabled )
    {
        ...
 
        //ColorScale specific
        if (name == "ColorCorrection")
        {
            //Update post effect parameters
            ColorCorrectionCompositorInstance.Red = scrollBarFloatParameters[0].Value;
            ColorCorrectionCompositorInstance.Green = scrollBarFloatParameters[1].Value;
            ColorCorrectionCompositorInstance.Blue = scrollBarFloatParameters[2].Value;
        }
    }
}

The code is complete. Now, compile it and start the project.

Posteffect menu

You can see that it is now possible to change the values of the color components in the posteffect options window.

ColorCorrection in action