Shaders! Large new documentation how to use shaders with Castle Game Engine, and new "shader libraries" (for now: to convert worldeye space comfortably), and new SetEffects methods

Animated volumetric fog shader effect
Color change effect
Fog shader effect
Fresnel and toon shading effects combined

We’ve done a number of improvements to our shaders support, and it starts with one big thing:

We have a new documentation how to use shaders with Castle Game Engine. Recommended reading! This documentation shows the most recommended way to use shaders in Castle Game Engine, combining simplicity with power:

  • It starts with up-to-date explanation of why we recommend to use “shader effects” (TEffectNode) to express your shaders.

  • It goes through simple practical examples: the most trivial shader, passing time to shader, passing texture to shader, converting eye<->world space (and what does it even mean).

  • Points interested readers to all the “advanced” topics and alternative ways to use shaders (sometimes more complicated).

We’ve also improvements to our engine API around shaders, to actually enable everything documented above:

  1. We added new SetEffects methods to easily connect TEffectNode with higher-level components that are core of our engine (like TCastleScene) in the simplest possible way. These are:
  2. We have improved our examples:
  3. We added property shaderLibraries (string list) to our TEffectNode. In Pascal you set it like this:
    MyEffectNode.SetShaderLibraries(['castle-shader:/EyeWorldSpace.glsl']);
    

    Right now, the castle-shader:/EyeWorldSpace.glsl is the only possible value you can put there. But the system may be more flexible in the future, allowing us to expose more GLSL libraries (from the engine, using castle-shader:/; other ideas may appear; note that you don’t need this to reuse shader setup in your own application, since you can just reuse own TEffectPartNode multiple times).

    Each “shader library” may define additional GLSL functions. It can also use PLUG_xxx of the shader, thus augmenting the rendering or computation. The uniform values necessary for the library are automatically passed by the engine, so you don’t need to know/do anything more to use it.

    The castle-shader:/EyeWorldSpace.glsl, in particular, defines 4 new GLSL functions, available in both fragment and vertex stages:

    vec4 position_world_to_eye_space(vec4 position_world);
    vec4 position_eye_to_world_space(vec4 position_eye);
    

    vec3 direction_world_to_eye_space(vec3 direction_world);
    vec3 direction_eye_to_world_space(vec3 direction_eye);

    Use them in your own shader code. Just make “forward declaration” for them first, like this (effect makes fog depending on point height in world space):

    vec4 position_eye_to_world_space(vec4 position_eye);
    

    // Save value obtained in PLUG_fragment_eye_space to use in PLUG_fragment_modify.
    vec4 vertex_world;

    // Get the vertex position in world space.
    void PLUG_fragment_eye_space(
    const vec4 vertex_eye,
    inout vec3 normal_eye)
    {
    vertex_world = position_eye_to_world_space(vertex_eye);
    }

    // Make lower things enveloped in fog (turn into gray).
    void PLUG_fragment_modify(
    inout vec4 fragment_color)
    {
    const float fog_y_start = 0.5;
    const float fog_y_max = -5.0;

    if (vertex_world.y < fog_y_start) {
    const vec4 bottom_fog_color = vec4(0.1, 0.1, 0.5, 1);
    float factor = max(0.0,
    (vertex_world.y - fog_y_max) /
    (fog_y_start - fog_y_max));
    fragment_color = mix(bottom_fog_color, fragment_color, factor);
    }
    }

    The X3D file demonstrating this feature is here.

    The Pascal example demonstrating this feature is here. The “Effect: cube map” is using this to get 3D direction from camera to rendered point, in world space, and then use this direction to query a cubemap (loaded from DDS).

    This deprecates: our Viewpoint.camera*Matrix events. Their usage in practice was only to pass new uniforms to the shaders, but they were complicated to use, needed X3D Viewpoint node, routes, passing events… The new thing is trivial to setup if you already have code dealing with shaders using TEffectPartNode, in Pascal or in X3D. And it is trivial to implement too, it just does literally what you expect, i.e. adds extra GLSL code and makes sure it receives proper input (uniforms).

Have fun using shaders! And if you like it, please consider donating to support the engine development.

2 Likes