The shading pipeline

The shading pipeline is the list of rendering passes performed at the camera level to produce an image. Graphical data are attached to the camera itself. Therefore, we 'render' the contents of a camera, and the produced result is positioned in the window that is hosting the camera. The viewpoint render list to which the camera has been attached defines the viewport information of the camera in the window.

The way geometries (meshes, lines, texts, etc...) are rendered on screen is defined by the contents of the material applied to the shapes. Each material can use a number of rendering passes, as shown by the schema below:

The additive default rendering pipeline

The principle used by the engine to generate an image is to cumulate the results of all rendering passes that have been defined on the material of the rendered geometry. Each pass results are blended with what has been already calculated by earlier rendering passes. In the example above, we have a bit of ambient colour, which is rendered as a first pass. Then, we add the contribution of each light source and finally we add a reflecting environment contribution. This one is a possible example of shading pipeline assembly. The container of all these rendering passes is the RED::IMaterial.

The rendering passes

The default pipeline uses three different rendering passes (see RED::MATERIAL_PASS):

There are more rendering passes that are worth mentioning here. We define indirect passes:

The point of these indirect passes is to store special GPU shaders that are needed for the rendering of geometries that are visible through reflections or refractions.

We also have a special pass called the RED::MTL_RAYTRACE. This pass is a rendering pass that contains shaders that may be needed by the engine to perform various calculation tasks on geometries - on purpose. For example, this pass may contain a RED::RayGIDiffuseShader that can tell to the engine the amount of diffuse colour to consider for the material during a global illumination calculation phase. Or, this pass could contain a RED::RayCutoffShader, which is a performance shader that'll be executed to minimize the amount of ray-traced calculations.

State shaders and rendering shaders

A material contains several shaders organized in rendering passes. A shader is a more or less simple program that gives to the programmer the control over the way pixels will appear on screen. Each one of these shaders is executed by REDsdk in a given order inside a material.

A material contains shaders organized in rendering passes

REDsdk have a very flexible shader pipeline letting the user writing his own shaders, either in ARB assembly or in OpenGL Shading Language (GLSL) for GPU rendering and arranging them in any order inside the rendering passes. Writing your own shaders for software rendering is also an option of the API. Finally, for those who do not want or have time to dig into the world of shader programming, REDsdk provides numerous built-in ones for any use.

Example of a simple GLSL shader program:

void main( void )
{
   gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );
}

The same program in assembly:

!!ARBfp1.0
MOV result.color, { 1.0, 0.0, 0.0, 1.0 };
END;

In software rendering, another kind of routine:

RED_RC AmbientShader( double                            ocolor[4],
                      const RED::ISoftRayContext&       rayctx,
                      const RED::ISoftShaderContext&    shaderctx,
                      const RED::ISoftRenderingContext& renderctx,
                      const RED::Version&               version )
{
  ocolor[0] = 1.0;
  ocolor[1] = 0.0;
  ocolor[2] = 0.0;
  ocolor[3] = 1.0;
  
  return RED_OK;
}

As you probably already guessed, each of these shader programs writes a constant red colour.

The RED::RenderShader

In REDsdk the shader program mechanism is handled by the RED::RenderShader class. Shaders are added to the material with the RED::IMaterial::AddShaderToPass function. By using this method, users have to specify in which RED::MATERIAL_PASS and at which position he wants to add the shader. The RED::IMaterial::RegisterShader must also be called to register the shader in the material.

The RED::RenderShader is an atomic class. It holds the shader programs and provides functions to define the geometrical inputs and the parameters. It is the base class of all the built-in and user-custom render shaders.

A complete list of all the built-in shaders is available on the page: Using built-in render shaders.

The RED::StateShader

Unlike rendering shaders that produce a colour, RED::StateShader objects are just here to mix the various results of the rendering shaders. They handled:

A state shader configures all the following render shaders in the pipeline until another state shader is met.

A full list of the state shader features is available here: Configuring a render shader using a state shader.

task

Task: Registering and adding a shader to a material pass

// Let:
// - iresmgr be the RED::IResourceManager;
// - imat be a RED::IMaterial.

// Create an ambient shader:
RED::RenderShaderAmbient ambient( RED::MTL_PRELIT, 
                                  RED::Color::WHITE, NULL, RED::Matrix::IDENTITY, RED::MCL_TEX0,
                                  RED::Color::BLACK, NULL, RED::Matrix::IDENTITY, RED::MCL_TEX0, 
                                  RED::Color::BLACK, NULL, RED::Matrix::IDENTITY, RED::MCL_TEX0, 
                                  RED::Color::WHITE, NULL, RED::Matrix::IDENTITY, RED::MCL_TEX0, 
                                  true, resmgr, rc );
RC_TEST( rc );

// Register the shader to the material:
RC_TEST( imat->RegisterShader( ambient, iresmgr->GetState() ) );

// Add the shader at the end of the pre-lit rendering pass:
RC_TEST( imat->AddShaderToPass( ambient.GetID(), 
                                RED::MTL_PRELIT, 
                                RED::LIST_LAST, 
                                RED::LayerSet::ALL_LAYERS, 
                                iresmgr->GetState() ) );

In this sample code, a simple built-in ambient shader is created. It is then registered to a material and added at the end (RED::LIST_LAST) of the prelit material pass (RED::MTL_PRELIT).

task

Task: Mimicking a direct shader setup for indirect rendering passes

Contrary to software rendering, the transparencies and reflections are not automatic in GPU mode. If you want to create such a custom material, in addition to the shaders in the direct material passes:

you also have to create shaders for indirect material passes:

These shaders will be called for all the calculations that are done after a first level of ray reflection or refraction.

In the next example, an ambient shader is built for direct prelit material pass and for indirect prelit material pass with the same setup:

// Ambient shader:
RED::RenderShaderAmbient ambient( RED::MTL_PRELIT,
                                  RED::Color::WHITE, NULL, RED::Matrix::IDENTITY, RED::MCL_TEX0,
                                  RED::Color::BLACK, NULL, RED::Matrix::IDENTITY, RED::MCL_TEX0,
                                  RED::Color::BLACK, NULL, RED::Matrix::IDENTITY, RED::MCL_TEX0,
                                  RED::Color::WHITE, NULL, RED::Matrix::IDENTITY, RED::MCL_TEX0,
                                  true, resmgr, rc );
RC_TEST( rc );

RC_TEST( imat->RegisterShader( ambient, iresmgr->GetState() ) );
RC_TEST( imat->AddShaderToPass( ambient.GetID(), RED::MTL_PRELIT, RED::LIST_LAST, RED::LayerSet::ALL_LAYERS, iresmgr->GetState() ) );

// Ambient indirect shader:
RED::RenderShaderAmbient ambientI( RED::MTL_INDIRECT_PRELIT,
                                   RED::Color::WHITE, NULL, RED::Matrix::IDENTITY, RED::MCL_TEX0,
                                   RED::Color::BLACK, NULL, RED::Matrix::IDENTITY, RED::MCL_TEX0,
                                   RED::Color::BLACK, NULL, RED::Matrix::IDENTITY, RED::MCL_TEX0,
                                   RED::Color::WHITE, NULL, RED::Matrix::IDENTITY, RED::MCL_TEX0,
                                   true, resmgr, rc );
RC_TEST( rc );

RC_TEST( imat->RegisterShader( ambientI, iresmgr->GetState() ) );
RC_TEST( imat->AddShaderToPass( ambientI.GetID(), RED::MTL_INDIRECT_PRELIT, RED::LIST_LAST, RED::LayerSet::ALL_LAYERS, iresmgr->GetState() ) );

Capturing intermediate results images

Each material pass fills two buffers:

The buffers content can be retrieved between each of the direct material pass and available as texture images for the next pipeline stages.

Render shaders in the RED::MTL_LIT material pass can access to the images resulting of the RED::MTL_PRELIT pass.

Render shaders in the RED::MTL_POSTLIT material passes can access to the images resulting of the RED::MTL_LIT passes.

Shader parameters can be created to transmit these texture images using the following references:

To have details about shader parameters, refer to the following page: Shader parameters.

task

Task: Checking the availability of needed input geometry channels

A material method allows to query about the mesh channels it uses: RED::IMaterial::GetUsedChannels.

This function returns a list of RED::MESH_CHANNEL needed by the material shaders. The list data are grouped by RED::LayerSet.

typedef RED::Vector< RED::MESH_CHANNEL > ChannelVector;
RED::Map< RED::LayerSet, ChannelVector > channels;

// Retrieve the mesh channel list from the material:
RC_TEST( imat->GetUsedChannels( channels ) );

// Get the channels for the 'all layer' layerset:
ChannelVector* chan = channels.find( RED::LayerSet::ALL_LAYERS );

bool usetex0 = false;
if( chan != NULL )
{
  // Loop through the needed mesh channels and search for the RED::MCL_TEX0 channel:
  for( int i = 0; i < chan->size(); ++i )
  {
    usetex0 = usetex0 || ( (*chan)[i] == RED::MCL_TEX0 );
  }
}

if( usetex0 )
{
  // Be sure this channel is filled in your geometry...
}

In this sample, the channel list was retrieved from the material. Then we looped through it to find if the material needs the RED::MCL_TEX0 channel.