The Flexible Rendering Pipeline
A rendering pipeline defines the steps which need to be taken to bring a 3d scene to the screen. While the graphics hardware has a specific rendering pipeline of its own (doing basic operations like projection, clipping and rasterization), we talk about a more high-level rendering pipeline here which defines in which way objects are rendered. It is often required to specify the order in which objects with certain properties are drawn. Alpha-transparent objects for example should usually be drawn after opaque objects so that the blending works correctly. Furthermore, it is very common in modern games to use postprocessing to improve the visual fidelity. Postprocessing effects expect the scene to be available in a render target bound as texture so that they can do some image-space processing on it. The order of these operations and which render targets are used as input for which postprocessing effects is all defined in the rendering pipeline.
In Horde3D, the rendering pipeline is data-driven and can be configured with pipeline resources which are some sort of xml scripts. A pipeline resource has two important sections: a setup section where intermediate buffers (render targets) that are declared and a command queue which defines the specific rendering steps.
The pipeline commands are organized in stages. A stage is a group of commands that can be enabled or disabled from the code, for example in order to activate or deactivate a depth of field postprocessing effect. The commands are used to set some special rendering states (like binding render targets and setting shader uniform values) and to specify which geometry should be drawn.
A Simple Pipeline Explained
Let's take a look at a basic pipeline:
As you can see, the above pipeline has two stages. The first draws the scene geometry while the second draws overlays which are usually used for on-screen GUI or HUD elements. The overlays could also be in the same stage as the other scene geometry but we want to be able to disable overlays from the application which is only possible for an entire stage.
The geometry contains four commands. The first command clears the framebuffer (color and depth buffer). This is very straightforward.
The second command draws the scene geometry. The purpose of this pass is to write the ambient term of all visible objects to the framebuffer. The DrawGeometry command loops through the list of all visible objects and renders them. DrawGeometry has two important parameters: context and class.
The context parameter specifies which shader context is used for drawing the objects. Each renderable scene object has a material applied which references a shader resource. In the Direct3D world, a shader resource would be known as effect (FX) file. A shader resource can conatain a collection of different hardware shader programs which are wrapped in so called shader contexts. A context is very similar to a technique in HLSL and CgFX. Each context has a unique name and defines the shader code which is used for rendering. So a single shader resource can contain multiple hardware shaders which is very convenient. The basic idea is that different shader code is required when drawing the same object in different pipeline stages, so the shader code is dependent on the current context in respect to the pipeline. That is the reason why the subshaders of a shader resource are called contexts. In the sample above the AMBIENT context is used for drawing the objects.
The second parameter of DrawGeometry is the material class which specifies what objects should be drawn. The class parameter acts as a filter. Each material can have a string that defines its class name, as seen in the following example. The class name can be an arbitrary identifier and is not predefined by Horde in some way.
If the class parameter of DrawGeometry is an empty string, all visible objects will be drawn. If the parameter contains a name, only objects that have a material with the corresponding class name will be drawn. A practical example are transparent objects which should be drawn after solid objects. In the ambient pass, we don't want to draw transparent objects so the material class is set to ~Transparent. The tilde in the class name acts as not operator. All objects which have a material with the class Transparent will be ignored when the engine loops through the list of visible objects in order to draw them.
The next command, DoForwardLightLoop instructs Horde to perform the lighting calculations and accumulate the light intensities in the framebuffer. We can specify a material class again in order to specify which objects should receive lighting. In our case, we don't want to do any lighting computations for transparent objects, so they are ignored with the not operator.
The second DrawGeometry command is finally rendering the transparent objects, including particles. In this pass we only want to draw the transparent geometry, so we choose Transparent as material class. Transparent objects usually use some special shader code which should be implemented in the TRANSPARENT context in our case. It is important that all transparent objects are tagged as such. This means that all objects that are supposed to be transparent need to set the class TRANSPARENT in their material resources.
As the last step, all overlays are drawn. We need to specify which context should be used when drawing the overlays. In our example we want to use a context called OVERLAY which needs to be be defined in the overlay shader resource.