Skip to content

Multiple render passes and Buffers

Patricio Gonzalez Vivo edited this page Apr 11, 2020 · 1 revision

Using the defines flags to use multiple buffer passes

You can use multiple buffers by forking the code using #ifdef BUFFER_[number], #if defined (BUFFER_[number]) and/or #elif defined (BUFFER_[number]). Then you can fetch the content of those buffers by using the uniform sampler2D u_buffer[number] texture. Ex.:

    uniform sampler2D   u_buffer0;
    uniform sampler2D   u_buffer1;

    varying vec2        v_texcoord;

    void main() {
        vec3 color = vec3(0.0);
        vec2 st = v_texcoord;

    #ifdef BUFFER_0
        color.g = abs(sin(u_time));

    #elif defined( BUFFER_1 )
        color.r = abs(sin(u_time));

    #else
        color.b = abs(sin(u_time));

        color = mix(color,
                    mix(texture2D(u_buffer0, st).rgb,
                        texture2D(u_buffer1, st).rgb,
                        step(0.5, st.x) ),
                    step(0.5, st.y));
    #endif

        gl_FragColor = vec4(color, 1.0);
    }

There is an extended example on examples/test_multibuffer.frag and examples/grayscott.frag.

Using the defines flags to draw the background

If you load a 3D model or set a shader without opacity you will notice the background is black by default (actually transparent in RaspberryPi).

It's possible to set a background by adding a #ifdef BACKGROUND check and adding your code there. Check the example examples/model_background.frag

uniform vec2 u_resolution;
uniform float u_time;

varying vec4 v_color;
varying vec3 v_normal;

float stroke(float x, float size, float w) {
    float d = step(size, x+w*.5) - step(size, x-w*.5);
    return clamp(d, 0., 1.);
}

vec2 ratio(in vec2 st, in vec2 s) {
    return mix( vec2((st.x*s.x/s.y)-(s.x*.5-s.y*.5)/s.y,st.y),
                vec2(st.x,st.y*(s.y/s.x)-(s.y*.5-s.x*.5)/s.x),
                step(s.x,s.y));
}

float rectSDF(vec2 st, vec2 s) {
    st = st*2.-1.;
    return max( abs(st.x/s.x),
                abs(st.y/s.y) );
}

void main(void) {
   vec4 color = vec4(1.0);
   vec2 st = gl_FragCoord.xy/u_resolution.xy;
   vec2 pixel = 1./u_resolution.xy;

#ifdef BACKGROUND
    st = ratio(st, u_resolution);

    color.rgb *= vec3(0.75, 0.0, 0.0) * step(0.5, fract((st.x - st.y - u_time * 0.1) * 20.));

    float sdf = rectSDF(st, vec2(1.0));
    color.rgb *= step(sdf, 0.7);
    color.rgb += vec3(1.0, 0.0, 0.0) * stroke(sdf, 0.75, 0.01);
#else
    color.rgb = v_color.rgb;
    float shade = dot(v_normal, normalize(vec3(0.0, 0.75, 0.75)));
    color.rgb *= smoothstep(-1.0, 1.0, shade);
#endif

    gl_FragColor = color;
}

Using the defines flags to draw the a postprocessing layer

Also when loading 3D models it's possible to add a postprocessing layer adding a #ifdef POSTPROCESSING to branch the logic of the shader. To apply a postprocessing layer you need to read the scene as a texture, this is saved on the uniform sampler2D u_scene; texture together with a depth render pass of the scene located in uniform sampler2D u_scene_depth;. Here is an example of a cheap DoF at examples/model_postprocessing.frag

uniform sampler2D u_scene;          // Scene RGB
uniform sampler2D u_scene_depth;    // Scene Depth

uniform vec2 u_resolution;
uniform float u_time;

varying vec4 v_position;
varying vec4 v_color;
varying vec3 v_normal;
varying vec2 v_texcoord;

float stroke(float x, float size, float w) {
    float d = step(size, x+w*.5) - step(size, x-w*.5);
    return clamp(d, 0., 1.);
}

vec2 ratio(in vec2 st, in vec2 s) {
    return mix( vec2((st.x*s.x/s.y)-(s.x*.5-s.y*.5)/s.y,st.y),
                vec2(st.x,st.y*(s.y/s.x)-(s.y*.5-s.x*.5)/s.x),
                step(s.x,s.y));
}

float rectSDF(vec2 st, vec2 s) {
    st = st*2.-1.;
    return max( abs(st.x/s.x),
                abs(st.y/s.y) );
}

float LinearizeDepth(float zoverw) {
	float n = 1.0; //
	float f = 20000.0;
	return (2.0 * n) / (f + n - zoverw * (f - n));	
}

void main(void) {
   vec4 color = vec4(1.0);
   vec2 st = gl_FragCoord.xy/u_resolution.xy;
   vec2 pixel = 1./u_resolution.xy;

#ifdef BACKGROUND
    // Background pattern
    st = ratio(st, u_resolution);

    color.rgb *= vec3(0.75, 0.0, 0.0) * step(0.5, fract((st.x - st.y - u_time * 0.1) * 20.));

    float sdf = rectSDF(st, vec2(1.0));
    color.rgb *= step(sdf, 0.7);
    color.rgb += vec3(1.0, 0.0, 0.0) * stroke(sdf, 0.75, 0.01);

#elif defined(POSTPROCESSING)

    // Get depth
    float depth = texture2D(u_scene_depth, st).r;
    depth = LinearizeDepth(depth) * 200.0;

    // Define focal point
    float focalDistance = 100.0;
    float focalRange = 50.0;
    depth = min( abs(depth  - focalDistance) / focalRange, 1.0);
    
    // Cheap box blur
    pixel *= 4.;
    color.rgb = texture2D(u_scene, st + vec2(pixel.x, 0.0)).rgb;
    color.rgb += texture2D(u_scene, st + vec2(0.0, pixel.y)).rgb;
    color.rgb += texture2D(u_scene, st + vec2(-pixel.x, 0.0)).rgb;
    color.rgb += texture2D(u_scene, st + vec2(0.0, -pixel.y)).rgb;
    color.rgb *= 0.25;

    // Mix blur and crisp scene images based on depth
    color.rgb = mix(color.rgb, texture2D(u_scene, st).rgb, 1.0 - depth);

    // Debug Depth
    // color.rgb = vec3(1.) * depth;
#else

    // Material of model
    color.rgb = v_color.rgb;
    float shade = dot(v_normal, normalize(vec3(0.0, 0.75, 0.75)));
    color.rgb *= smoothstep(-1.0, 1.0, shade);
#endif

    gl_FragColor = color;
}