Technology Demo

Compositor Demo

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Spot for images

Intro

This demo will show you how to use a compositor to control how a scene renders. It will give the basics of shaders as well.

Compositor

The compositor is the program that applies post-rendering effects to the scene. In our example, we will be taking a scene and turning it gray scale. The compositor will take a render of the scene and send it through a series of shaders.

To begin, we need to create a new project and create a few new folders named Compositors, Materials, Shaders and Textures. Inside of the Textures folder will simply be a file you wish to use as a texture for the cube that will be placed in the scene.

In the Compositors folder, create a new file called test.compositor, which will hold the following code.

compositor Grayscale
{
    technique
    {    	
    	texture rt0 target_width target_height PF_R8G8B8
    	target rt0
    	{
    	   input previous
    	}
        target_output
        {
           input none
           pass render_quad
           {
               material Grayscale
               input 0 rt0
           }
        }
    }
}
compositor Sepia
{
	technique
	{
		texture rt0 target_width target_height PF_R8G8B8
    	target rt0
    	{
    		input previous
    	}
        target_output
        {
            input none
            pass render_quad
            {
                material Sepia
                input 0 rt0
            }
        }
	}
}

This section of the code does several things. It takes a render of the scene and stores it in a texture called rt0, and then defines the target as rt0 and tells it that its input is the previous render.

The target_output is given nothing as input, so that it starts as a clear frame. In the first compositor, tt has a single pass that applies the Grayscale material to the previously rendered scene, rt0. In the second, it has the Sepia material.

Materials

A single material file called test.material will be the only material required for this project. It will contain the information for the vertex shader and the pixel shader, as well as the material that will be applied to the cube in the scene.

First, we will need to define the vertex program. A vertex program can be used to manipulate each vertex in the scene. The vertex shader we're using doesn't do this, but it could be used to create a ripple effect or something else of that nature.

Here, we tell the program to use basequad.cg as its vertex shader, which is a very basic

vertex_program BaseQuadVS cg
{
   source Shaders/basequad.cg
   entry_point main
   profiles vs_1_1 arbvp1 vp20
   default_params{param_named_auto worldViewProj worldviewproj_matrix}
}

Next, we need to reference a pixel shader for our program, which will go pixel-by-pixel in the image and It tells the program to use the shader "gray.cg" as its pixel shader, and defines the entry point and profile.

fragment_program GrayPS cg
{
   source Shaders/gray.cg
   entry_point main
   profiles ps_2_0 arbfp1 fp20
}

We also have another pixel shader for a Sepia tone.

fragment_program SepiaPS cg
{
   source Shaders/sepia.cg
   entry_point main
   profiles ps_2_0 arbfp1 fp20
}

Next, we need to define the material that will contain both the vertex and pixel shader, referred to here as a vertex program and fragment program respectively. It contains a single technique and pass, though more can be added. The first technique listed is the primary technique and is the first to be applied. Texture unit is defined but left empty because the texture that is passed in will be the render of the scene.

material Grayscale
{
   technique
   {
      pass
      {
         vertex_program_ref BaseQuadVS {}
         fragment_program_ref GrayPS {}
         texture_unit{}
      }
   }
}

Here is a similar material for a Sepia tone to be applied.

material Sepia{
   technique{
      pass{
         vertex_program_ref BaseQuadVS {}
         fragment_program_ref SepiaPS {}
         texture_unit{}
      }
   }
}

And here is the material for the cube. All that we do is tell it to use the cube.jpg picture as the texture for the cube.

material Cube
{
   technique
   {
      pass
      {
         texture_unit{texture Textures/cube.jpg}
      }
   }
}

Shaders

Two shaders will be required for this project. One is the vertex shader, called basequad.cg, and the other is the pixel shader, called gray.cg. Create both of these files in the Shaders folder by right clicking on Shaders and selecting New > File.

We'll start with basequad.cg, which is a very basic vertex program.

void main(in float4 inPos : POSITION,
          out float4 pos : POSITION,
          out float2 uv0 : TEXCOORD0,
          uniform float4x4 worldViewProj)
{
    pos = mul(worldViewProj, inPos);
    inPos.xy = sign(inPos.xy);
    uv0 = (float2(inPos.x, -inPos.y) + 1.0f) * 0.5f;
}

Next, we have the gray.cg fragment program, which takes in the coordinate of a vertex.

sampler2D test: TEXUNIT0;
float4 main(float2 texCoord: TEXCOORD0) : COLOR
{ 
   float4 color = tex2D(test,texCoord);
   return (color.r + color.g + color.b)/3.0f;
}

This program takes in a coordinate for a point in the image, and above the main function a sample is defined. It will take a sample at the given coordinate, so that a new tex2D, named color, can be created.

The program returns the average of the red, green, and blue of the color at that given point, resulting in a gray pixel.

Next, we have a similar fragment program to apply a sepia tone, in a file called sepia.cg.

sampler2D test: TEXUNIT0;
float4 main(float2 texCoord: TEXCOORD0) : COLOR
{ 
   float4 color = tex2D(test,texCoord);
   color = (color.r + color.g + color.b)/3.0f;
   color.r += 0.07645;
   color.g += 0.0655;
   color.b += 0.001;
   return color;
}

Using the Compositor

Now that all of the set up for the files has been completed, we can go to our Root.lua file. For this example, we'll set the background to a generic color. You can set this directly under where the camera is created.

stage:getViewport(0).backgroundColor = luster.Color(.5,.8,1,1)

Next, we create a cube, apply our cube material to it, and set up the usual movement controls for it. Next, we can apply the compositor.

local compositor = luster.resources.Compositor("Sepia")
stage:getViewport(0):addCompositor(compositor)
stage:getViewport(0):setCompositorEnabled(compositor)

We set a compositor variable to be Sepia - though you can also use Grayscale and compare the different effects - and then add it to the viewport. Then, we enable the compositor, and we can run our program to see the effects.