The Flexible Rendering Pipeline

Introduction

The rendering pipeline defines the steps which are necessary to bring the abstract 3D data to a visible result on the screen. Up to version 0.9, Horde3D used deferred shading with lighting as a post processing step as the primary rendering technique. The subsequent version 0.10 introduces a new flexible rendering pipeline which makes it possible to employ a plenty of different rendering techniques, including standard forward rendering and deferred shading. The system allows to define several render targets and to specify render commands to fill the targets with data. This gives much power and flexibility to the engine and enables the creation of most post processing effects like HDR, motion blur or depth of field as well as the usage of different rendering algorithms.


System overview

Horde is heavily based on shaders which are small programs that are executed on the graphics card at different stages of the hardware pipeline. Horde uses vertex and fragment shaders. Vertex shaders influence directly the geometry which is rendered and determine e.g. the position and texture coordinates of the vertices. The fragment shaders are used for calculating the pixel colors in the rasterization process. Shader code in Horde is specified in the OpenGL Shading Language (GLSL). The engine has a XML based shader format which makes it possible to define different contexts. A context of a Horde shader is defined for a situation in the rendering process where the shader is executed. For example a shader usally has a shadowmap context which is used when shadows are generated and a lighting context which is responsible for interactions of an object with light sources. For the rendering process shaders usually require some input data. This consists on the one hand of the geometric data of a model like the vertex positions, normals and texture coordinates. On the other hand there are textures used for rendering and arbitrary variables, so called uniforms, which can be defined by the user. For example a uniform could be used to define a force vector to do some wind physics for a tree model in the vertex shader. This mutable data is bound to the shader via the concept of materials. A material in Horde consists of a shader and a list of texture maps and uniforms which are assigned to that shader.


Lighting and shadows

Horde supports basically two different approaches for lighting namely forward and deferred shading. Forward shading is the standard technique that is currently used in most applications. With this technique the geometry is rendered once for each light source using a special fragment shader that calculates the light contribution. The disadvantage here is of course that the geometry has to be drawn several times which can result in poor performance in scenes with many polygons and light sources. Deferred shading in contrast does the lighting as a post processing step. The idea is to store some attributes for each pixel on the screen in a special buffer often called the G-Buffer. These attributes usually include the position of a fragment, the normal and its color. To do the actual lighting it is only necessary to draw a fullscreen quad since the required information can easily be read from the G-Buffer. The advantage is that the lighting performance is completely independent of the geometry complexity now since each light just requires drawing a screen-space quad. Unfortunately this approach has also its drawbacks as it is difficult to do anti-aliasing or handle translucent geometry. Discussing the two techniques in detail here would be out of the scope of this manual so please refer to one of the many online and book resources available for this topic.

Light sources in Horde are defined as scene nodes. Each light node has two special attributes called lightingContext and shadowContext. The values of these attributes correspond to shader context names defined in the shader resources. When Horde is instructed to perform forward lighting the engine first builds a list of the geometry that needs to be drawn. After that it draws each object from that list using the shader context that is specified for the light source. If the context cannot be found for a material, the object using this material is ignored and thus not rendered. This is basically working the same way for doing the lighting and building the shadow map. For deferred shading you also need to specify a material for the light source. The shader defined in that material is used to draw the screen-space quads where the light's lightingContext attribute specifies the shader context which is used. The calculation of the shadows is working the same way as for forward shading where all geometry is rendered to the shadow map using the shadowContext. When doing forward shading you could also specify a material for a light source. The shader is ignored in this case but it is possible to bind several textures which can for example be used to create some sort of slide-projector.

With the shader context system it is possible to define arbitrary light source types. The behavior of a light (e.g. directional or spot) and its interaction with a material are defined entirely in the shaders. If you want to create a new type of light source you just have to assign a name to the lightingContext attribute and define an appropriate shader context for all the materials (respectively shaders) that shall interact with that light source.


Pipeline configuration

The flexible rendering pipeline allows to define render targets and commands which determine the steps taken to render the scene. The commands are specified within a XML file. Most rendering commands use the attributes class and context. The class is defined in the materials and determines what geometry should be rendered. It is possible to use the tilde operator ~ as a logical NOT meaning that all geometry except the one with the specified material class will be drawn. The context is finally available for specifying the rendering technique which should be used for the current draw call.

Pipeline syntax

The following XML elements and attributes are supported.

Pipeline root element of the document {1}
EngineConfig configuration of engine options {1}
trilinearFiltering see EngineOptions {optional}; additional values: false, true
anisotropyFactor see EngineOptions {optional}
texCompression see EngineOptions {optional}; additional values: false, true
loadTextures see EngineOptions {optional}; additional values: false, true
fastAnimation see EngineOptions {optional}; additional values: false, true
occlusionCulling see EngineOptions {optional}; additional values: false, true
shadowMapSize see EngineOptions {optional}
debugViewMode see EngineOptions {optional}; additional values: false, true
Setup initialization section of pipeline {0,1}
RenderTarget definition of a render target; child of Setup element {*}
id unique name of the render target {required}
depthBuf flag specifying whether depth buffer is used for target {required}; possible values: true, false
numColBufs number of color buffers {required}; possible values: 0, 1, 2, 3, 4
format pixel format of render target {optional}; possible values: RGBA8, RGBA16F, RGBA32F; default: RGBA8
bilinear flag specifying whether bilinear filtering is enabled for color buffers {optional}; possible values: true, false; default: false
width width of render target in pixels where 0 means width of the main framebuffer {optional}; default: 0
height height of render target in pixels where 0 means height of the main framebuffer {optional}; default: 0
scale scale factor which is multiplied with the size of the render target {optional}; default: 1.0
maxSamples the maximum number of samples used when anti-aliasing is enabled {optional}; default: 0
CommandQueue ordered list of commands {0, 1}
Stage definition of a set of render commands; child of CommandQueue element {*}
id unique name of the stage {required}
enabled flag indicating whether stage is enabled by default {optional}; default: true
link material resource used to bind stage-specific data {optional}; default: empty string
SwitchTarget command for setting the currently active render target to which data is rendered; child of Stage element {*}
target name of the render target which was defined in the Setup section or empty string to bind main framebuffer {required}
BindBuffer command for binding a color or depth buffer of a render target as texture map; child of Stage element {*}
texUnit texture unit to which buffer is assigned {required}; possible values: 0-11
target name of render target {required}
bufIndex index of color buffer or 32 as special value for binding the depth buffer {required}
ClearTarget command for clearing the currently bound render target; child of Stage element {*}
depthBuf flag specifying whether depth buffer is cleared {optional}; possible values: true, false; default: false
colBuf0 flag specifying whether first color buffer is cleared {optional}; possible values: true, false; default: false
colBuf1 flag specifying whether second color buffer is cleared {optional}; possible values: true, false; default: false
colBuf2 flag specifying whether third color buffer is cleared {optional}; possible values: true, false; default: false
colBuf3 flag specifying whether fourth color buffer is cleared {optional}; possible values: true, false; default: false
col_R red component of clear color {optional}; default: 0.0
col_G green component of clear color {optional}; default: 0.0
col_B blue component of clear color {optional}; default: 0.0
col_A alpha component of clear color {optional}; default: 0.0
DrawGeometry command for rendering the scene geometry; child of Stage element {*}
context name of the shader context used for rendering {required}
class material class used for including/excluding objects {optional}; default: empty string, meaning all classes
DrawOverlays command for rendering all overlays; child of Stage element {*}
context name of the shader context used for rendering {required}
DrawQuad command for drawing a fullscreen quad to the screen; child of Stage element {*}
material material resource used for rendering {required}
context name of the shader context used for rendering {required}
DoForwardLightLoop command for performing forward lighting by rendering all affected geometry; the default shader context used for rendering is the lighting context attribute of the corresponding light source; child of Stage element {*}
class material class used for including/excluding objects {optional}; default: empty string, meaning all classes
context shader context used for doing lighting {optional}; default: empty string, meaning context assigned to light source
DoDeferredLightLoop command for performing deferred lighting by drawing screen-space quads; the default shader context used for rendering is the one which is stored as attribute of the corresponding light source; child of Stage element {*}
context shader context used for doing lighting {optional}; default: empty string, meaning context assigned to light source
SetUniform command for setting a material uniform to specified values; child of Stage element {*}
material material resource which contains the uniform to be set {required}
uniform name of the uniform {required}
a value of the first component {optional}; default: 0.0
b value of the second component {optional}; default: 0.0
c value of the third component {optional}; default: 0.0
d value of the fourth component {optional}; default: 0.0


Sample showing simple deferred shading pipeline

<Pipeline>

    <Setup>
        <RenderTarget id="GBUFFER" depthBuf="true" numColBufs="3" format="RGBA16F" scale="1.0" />
    </Setup>
        
    <CommandQueue>
        <Stage id="Attribpass">
            <SwitchTarget target="GBUFFER" />
            <ClearTarget depthBuf="true" colBuf0="true" />
            <DrawGeometry context="ATTRIBPASS" class="~Translucent" />
        </Stage>
		
        <Stage id="Lighting">
            <SwitchTarget target="" />
            <ClearTarget colBuf0="true" />
            <BindBuffer texUnit="8" target="GBUFFER" bufIndex="0" />
            <BindBuffer texUnit="9" target="GBUFFER" bufIndex="1" />
            <BindBuffer texUnit="10" target="GBUFFER" bufIndex="2" />
			
            <DrawQuad material="light.material.xml" context="AMBIENT" />
            <DoDeferredLightLoop />
        </Stage>
		
        <Stage id="Overlays">
            <DrawOverlays context="OVERLAY" />
        </Stage>
    </CommandQueue>
    
</Pipeline>



Predefined GLSL attributes and uniforms

Horde defines some standard uniforms and attributes which can be used by shaders. The engine automatically detects which input data is required and binds it to the shader programs.

Note: Not all uniforms and attributes are available for every pipeline step, e.g. light source parameters are only available when doing lighting calculations and particle specific data only when rendering emitters.

General vector/matrix uniforms

uniform mat4 worldMat matrix used for transforming vertex positions of currently rendered mesh to world space
uniform mat3 worldNormalMat matrix used for transforming tangent space basis of currently rendered mesh to world space
uniform vec3 viewer position of the viewer (virtual camera)
uniform vec4 lightPos position of the light source in xyz-components and radius in w-component
uniform vec3 lightDir direction vector of the light source
uniform vec3 lightColor (diffuse) color of the light source
uniform float lightCosCutoff cosine of the light's field of view (FOV) presumed it is a spotlight
uniform vec4 shadowSplitDists split distances determining which of the four shadow maps has to be sampled
uniform mat4 shadowMats[4] light transformation matrices for individual shadow maps
uniform float shadowMapSize size of the shadow map texture in pixels
uniform float shadowBias bias used for shadow mapping to reduce precision issues
uniform vec4 skinMatRows[75*3] first three rows of skinning matrices for skeletal animation; fourth row is always (0, 0, 0, 1)
uniform vec2 frameBufSize dimensions (width and height) of the currently active frame buffer


Particle specific vector/matrix uniforms

uniform vec3 parCorners[4] four corner positions of particle billboard
uniform vec3 parPosArray[64] position array of particle batch
uniform vec2 parSizeAndRotArray[64] combined size and rotation array of particle batch
uniform vec4 parColorArray[64] color array of particle batch


Sampler uniforms

uniform sampler2D shadowMap shadow map texture
uniform sampler[2D/Cube] tex0 texture unit 0
uniform sampler[2D/Cube] tex1 texture unit 1
uniform sampler[2D/Cube] tex2 texture unit 2
uniform sampler[2D/Cube] tex3 texture unit 3
uniform sampler[2D/Cube] tex4 texture unit 4
uniform sampler[2D/Cube] tex5 texture unit 5
uniform sampler[2D/Cube] tex6 texture unit 6
uniform sampler[2D/Cube] tex7 texture unit 7
uniform sampler[2D/Cube] tex8 texture unit 8
uniform sampler[2D/Cube] tex9 texture unit 9
uniform sampler[2D/Cube] tex10 texture unit 10
uniform sampler[2D/Cube] tex11 texture unit 11


General vertex attributes

attribute vec2 texCoords0 first set of texture mapping coordinates
attribute vec2 texCoords1 second set of texture mapping coordinates
attribute vec3 normal normal vector of vertex
attribute vec3 tangent tangent vector of vertex
attribute vec3 bitangent bitangent vector of vertex
attribute vec4 joints four joint indices referencing to skinning matrices
attribute vec4 weights four vertex weights for the four joint indices


Particle specific vertex attributes

attribute float parIdx index of current particle in position, size and color arrays
attribute float parCornerIdx index of current particle's corner in corners array


Other data available in vertex shader

The vertex position is available through the attribute gl_Vertex. The camera transformation is stored in the modelview matrix (gl_ModelViewMatrix) and the projection matrix in gl_ProjectionMatrix. All overlays have their texture coordinates stored in gl_MultiTexCoord0.

Copyright © 2006-2008 Nicolas Schulz