Skip to content

Effects

tobspr edited this page Oct 31, 2015 · 22 revisions

The effects system was introduced to make creating custom shaders easier. An effect file uses builtin templates and injects shader code at predefined "hooks". That way the effects are compatible with different pipeline versions, even if the template code changes.

Each effect is a yaml file, containing information which shader templates to use and shader code to be injected into the hooks. The default effect file looks like this:

Vertex:
    template: default

Fragment.GBuffer:
    template: default

Fragment.Shadows:
    template: default

Fragment.Voxelize:
    template: default

This is the simples possible effect, and it does nothing more than including all the default templates. You can find the templates in Shader/Templates/. As you can see, there are 4 sections, containing the definition for the vertex shader, and 3 stages, namely the G-Buffer stage, the stage used for rendering shadows, and the stage used to voxelize the scene. The vertex shader is shared for all stages.

Specifying additional requirements

For each stage, you can list additional requirements, like additional inputs or outputs, and includes. For example, you could modify the vertex stage to something like this:

Vertex:
    template: default
    dependencies:
        - Includes/SomeRequirement.inc.glsl
    inout:
        - uniform vec3 myCustomInput;
        - in vec4 p3d_Tangent;
        - out vec3 myCustomPerVertexOutput;

That way you can include other glsl files, and add in- and outputs.

Injecting custom code

Each template defines several hooks, where you can inject code. Lets have a look at some part of the default fragment shader template:

    Material m;
    // ...
    m.specular = specular;
    m.roughness = roughness;
    
    %MATERIAL%

    render_material(m);

You can see there is a hook defined (named "material", hooks are always converted to lowercase), and that's where you can inject your custom code. If you, for example, would like to set the materials roughness and specular value to some fixed value, you could do:

Fragment.GBuffer:
    template: default
    inject:
        material:
            m.roughness = 0.123;
            m.specular = 0.456;

The code specified at that section gets inserted into the template, to build the final shader.

Debugging the created shaders

If you get a shader error for example, or want to see how your effects gets translated into a shader, you can have a look at the temporary generated files. First, you have to make sure that you set a physical write path (see Mount Manager). Then, while the pipeline is running, head over to that temporary path. You will see a lot of generated shader files, including the shader file generated for your effect. If you look at that file, you will see a lot of comments, telling the line numbers, and whether the line was generated from the template or the effect file. For example, the effect from the previous section got translated to:

                 ...
/* T 0026 */     Material m;
                 ...
/* T 0031 */     m.specular = specular;
/* T 0032 */     m.roughness = roughness;

/* Hook material */
/* E 0000 */     m.roughness = 0.123;
/* E 0001 */     m.specular = 0.456;

/* T 0036 */     render_material(m);
/* T 0037 */ }

As you can see, T specifies a line from the template, and E specifies a line inserted from the effect.