Creating a Custom Shader for Post-Processing
Unigine engine allows you to create your own post-effects by writing custom shaders. To write post-effect shaders, you should use the same workflow as for deferred and forward rendering passes: create the material, write vertex and fragment shaders.
This tutorial explains how to create a post-effect material, write shaders for it (both vertex and fragment), add a parameter to the material to be able to specify the value from the UnigineEditor.
See Also#
- The article on Material Settings
- The article on Custom Materials
Create a Material#
As in all other shaders tutorials, you should create the material first. Let's add a new base material to your project.
To create post-effect material, you should specify the post pass for shaders and textures.
The material will have the following structure:
<?xml version="1.0" encoding="utf-8"?>
<base_material version="2.0" name="custom_post" editable="0">
<!-- Post-effect shaders -->
<shader pass="post"
defines="BASE_POST"
vertex="shaders/vertex/post.vert"
fragment="shaders/fragment/post.frag"/>
<!-- Textures -->
<texture name="color" pass="post" unit="0" type="procedural"/>
<texture name="dirt" pass="post" unit="1" anisotropy="1" workflow="0" tooltip="Dirt color texture">core/textures/water_global/foam_d.dds</texture>
<!-- Parameters -->
<slider name="grayscale_power" shared="1" min="0.0" max="1.0" flags="max_expand">0.5</slider>
<slider name="dirt_power" shared="1" min="-1.0" max="1.0" flags="max_expand">0.5</slider>
</base_material>
The key features of this post material are:
- Added shaders and textures for post pass.
- Added the shared grayscale_power and dirt_power parameters.
Save the new material as custom_post.basemat file to the data folder.
Create Vertex Shader#
Since we write a simple shader example, let's write a simple shader like the vertex shader for the deferred rendering pass.
- Write the shader code in the plain text editor:
// Include the UUSL header #include <core/shaders/common/common.h> // Input data struct STRUCT(VERTEX_IN) INIT_ATTRIBUTE(float4,0,POSITION) // Vertex position INIT_ATTRIBUTE(float4,1,TEXCOORD0) // Vertex texcoords INIT_ATTRIBUTE(float4,2,COLOR0) // Vertex color END // Output data struct STRUCT(VERTEX_OUT) INIT_POSITION // Output projected position INIT_OUT(float2,0) // Texcoords (x and y only) END MAIN_BEGIN(VERTEX_OUT,VERTEX_IN) // Set output position OUT_POSITION = getPosition(IN_ATTRIBUTE(0)); OUT_DATA(0).xy = IN_ATTRIBUTE(1).xy; MAIN_END // end
You should add a new line (press Enter) after closing the instruction (after MAIN_END command). - Save the shader file as post.vert to the data/shaders/vertex folder.
The code of the vertex shader is simple since we don't need to work with the geometry.
Create Fragment Shader#
This section contains instruction how to create a fragment shader (also known as pixel shader).
To create the fragment shader for post-process pass, perform the following:
- Open a plain text editor, and write the following:
// Include the UUSL fragment shader header #include <core/shaders/common/fragment.h> // Define the texture of the scene INIT_TEXTURE(0,TEX_SCENE) INIT_TEXTURE(1,TEX_DIRT) // Input values STRUCT(FRAGMENT_IN) INIT_POSITION // Projected position INIT_IN(float2,0) // Texcoords END // Define the grayscale_power parameter CBUFFER(parameters) UNIFORM float grayscale_power; UNIFORM float dirt_power; END MAIN_BEGIN(FRAGMENT_OUT,FRAGMENT_IN) // Get the UV float2 uv = IN_DATA(0); // Get the scene color float4 scene_color = TEXTURE_BIAS_ZERO(TEX_SCENE,uv); // Get the dirt color float4 dirt_color = TEXTURE_BIAS_ZERO(TEX_DIRT,uv); // Calculate the grayscale float3 gray_scene_color = dot(float3(0.3f, 0.59f, 0.11f), scene_color.rgb); scene_color.rgb = lerp(scene_color.rgb,gray_scene_color,grayscale_power); // add some dirt OUT_COLOR = scene_color+dirt_color*dirt_power; MAIN_END // end
You should add a new line (press Enter) after closing the instruction (after MAIN_END command). - Save the shader file as post.frag to the data/shaders/fragment folder.
Well, let's clarify what is under the hood of this fragment shader:
- We get the texture which was specified in the post-effect material.
- By applying a standard grayscale equation, we change the color of the scene.
- By using lerp function (which performs a linear interpolation), we add the custom grayscale_power parameter to adjust the grayscale power.
- We also get the dirt texture and apply it to the final scene color to simulate dirn on camera lens (can also be used for vignette effect etc.)
- A custom dirt_power parameter to adjust intensity of the dirt texture (its impact on the final image).
See Also#
- Grayscale article on Wikipedia.
Editing the Material#
Material has been created, shaders have been written, it's time to use it in the project!
- Open UnigineEditor and launch your project.
- Create a new material by inheriting from the recently created one in the Materials Hierarchy window.
- Open the Settings window by choosing Windows -> Settings from the main menu
- In the Settings window choose Runtime -> World -> Render -> Screen Space Materials and specify the name of the child post material in the Post field.
The grayscale post-effect will be applied.
- Select your material in the Materials window. Then in the Parameters window select Parameters tab.
The final scene.