Skip to content

Render Stages

tobspr edited this page Apr 15, 2016 · 10 revisions

Render Stages are a core mechanism of the pipeline. The pipeline collects all render stages, forms a pipe out of them, and executes them in order, to get the final result.

TODO: Add graphic

Each render stage can specify which pipes and inputs it needs, and also which pipes and inputs it produces. Render stages can also modify a pipe, by simply specifying the same pipe as input and output pipe.

Example

A simple render which takes as input SomePipe1 and SomePipe2, and produces SomePipe3 looks like this:

class MyStage(RenderStage):
    
    """ Specify all required pipes here. They will be made available as
    a shader input to all contained targets """
    required_pipes = ["SomePipe1", "SomePipe2"]

    """ Specify all required inputs here. They will be made available
    as a shader input aswell. """
    required_inputs = ["some_input", "another_input"]

    def create(self):
        """ This method gets called when the stage actually gets created.
        You should setup all RenderTargets here. """

        # Construct a single Render Target, see the RenderTarget API
        self.target = self.create_target("My Target")
        self.target.add_color_texture(bits=16)
        self.target.prepare_offscreen_buffer()

    def get_produced_pipes(self):
        """ This method tells the pipeline which pipes are produced / modified
        by this stage. The key is the name of the pipe, and the value should be
        a texture handle """
        return {"SomePipe3": self._target.color_tex}

    def set_shaders(self):
        """ This method gets called after the create method, as well as on
        manual shader reload. It should set all required shaders on the 
        render targets """
        # my_stage.frag.glsl should be located in the plugin directory at
        # shader/my_stage.frag.glsl
        self.target.shader = self.load_plugin_shader("my_stage.frag.glsl")

Specifying the Stage order

The pipeline somehow has to know at which point of the rendering process your stage belongs to. This is configured in the config/stages.yaml. You should insert the class name of your stage somewhere inbetween there (where it fits, and you want it to get executed).

Writing stage shaders

Writing shaders for your stages is pretty straightforward. Assuming you created a RenderStage like the example above, which takes SomePipe1 and SomePipe2 to produce SomePipe3 (e.g. by adding both), then your shader could look like this:

#version 400

// Base render pipeline configuration
#pragma include "render_pipeline_base.inc.glsl"

// Input from the vertex shader, range 0 .. 1
in vec4 texcoord;

// Fragment output
out vec4 output;

// Pipe inputs
uniform sampler2D SomePipe1;
uniform sampler2D SomePipe2;

void main() {
    vec4 pipe1_value = textureLod(SomePipe1, texcoord, 0);
    vec4 pipe2_value = textureLod(SomePipe2, texcoord, 0);
    output = pipe1_value + pipe2_value;
}