WebGL Textures and Vertices eBook Screen Shots

WebGL Post Processing

Details: Short Tutorial

Overview Framebuffers Depth Textures Blur Shaders Vertex Shader Fragment Shader Summary

Overview

This article discusses a few details regarding post processing with WebGL. Most of Seven Thunder Software's WebGL Post Processing examples render to a frame or depth buffer, switch shader programs, process the rendered texture with new shaders, then display the result to the canvas. The first program prepares the basic rendered frame. The second program prepares post processing.

This tutorial provides information regarding rendering to a framebuffer and rendering to a depth texture. The end of the article includes the entire blur post processing vertex and fragment shaders used in the Cube Blurs with Post Processing example. The blur post processing shaders execute after the basic rendering shaders.

Frame Buffers

Create and bind a frame buffer with the following two WebGL methods. Method createFramebuffer() initializes a frame buffer. Method bindFramebuffer() binds the framebuffer to a target. Constant gl.FRAMEBUFFER collects or stores data for color, alpha, depth and stencil buffers. Variable fb is a frame buffer. Variable gl is a WebGLRenderingContext.

var fb = gl.createFramebuffer();

gl.bindFramebuffer(
 gl.FRAMEBUFFER, 
 fb
);
  

Most of Seven Thunder Software's WebGL Post Processing Techniques employ two programs for post processing. The first program includes shaders which render the scene. The second program includes shaders which process the scene.

During rendering first activate the program which renders the scene with basic rendering shaders. Variable programRender is a WebGLProgram with valid vertex and fragment shaders.

gl.useProgram(
 programRender
); 
  

Second bind the frame buffer. Then render with the basic rendering shaders. The scene renders to the framebuffer rather than the canvas.

gl.bindFramebuffer(
 gl.FRAMEBUFFER, 
 fb
);

Third activate the program used for post processing. Variable programPostProcess is a WebGLProgram with valid vertex and fragment shaders. The Blur Shaders section demonstrates vertex and fragment shaders assigned to programPostProcess. The Blur Shaders perform post processing.

gl.useProgram(
 programPostProcess
);

Fourth assign the framebuffer's texture unit to the sampler accessed within the post process shaders. Variable TEX_COLOR in the following listing is the number of the texture unit assigned to the framebuffer. Variable uSampler0 is the location of the Sampler2D within the post processing shader. Sampler2D uSampler0 samples values from the framebuffer. The goal is to modify the pre-rendered scene with the new shaders.

gl.uniform1i
(
 uSampler0, 
 TEX_COLOR
); 

Last render a square with the post processing shaders. The square displays on the canvas. The first program renders, any range of mesh elements, to the frame buffer. The second program renders data from the framebuffer to a square quad for display on the canvas.

Most of Seven Thunder Software's WebGL Post Processing Techniques use a set of vertices which render a simple quad with various post processing shaders.

Depth Textures

This section provides a few tips regarding how to use WebGL depth textures.

First determine if depth textures are available within the current browser. Variable gl is a WebGLRenderingContext. Method getExtension() returns a boolean value. Parameter WEBGL_depth_texture requests depth texture availability. In the following listing, if depth texturing is available then bDepthTextureExtension equals true.

this.bDepthTextureExtension = gl.getExtension(
 "WEBGL_depth_texture"
);
  

Next enable depth testing with the WebGL API method enable(). Call gl.enable(gl.DEPTH_TEST) on the WebGLContext.

Check WebGL context attributes to verify depth testing has been enabled. The following WebGLContext attributes include depth: true. If depth: true then depth testing is both available and enabled.

WebGLContext attributes:
alpha:true
antialias:true
depth:true
stencil:false
premultipliedAlpha:true

Depth Texture Tips

Currently Windows Phone 8.1 default Web browser doesn't support depth textures. The default color shader displays. Sometimes it's helpful to let the user know depth testing isn't available. Other times it's helpful to include fall back rendering.

Blur Shaders

This section includes vertex and fragment shaders used to blur a scene.

Vertex Shader

The following vertex shader first multiplies the perspective matrix, transformation matrix, and current vertex attribute. The product is assigned to built in vector gl_Position. Last the vertex shader assigns the current texel attribute to the varying v_tex_coord0 for processing in the GPU and fragment shader.

More Efficent Options

Post processing often simply renders to a square plane or quad. Possibly leave out the perspective projection matrix. Perhaps use a constant transformation matrix.

attribute vec4 a_position;   
attribute vec2 a_tex_coord0;
varying vec2 v_tex_coord0;
           
uniform mat4 um4_matrix;
uniform mat4 um4_pmatrix; 
           
void main(void) {

gl_Position = um4_pmatrix 
 * um4_matrix 
 * a_position; 
 
v_tex_coord0 = a_tex_coord0;      

Fragment Shader

One method to blur an image involves mixing color samples taken from the same image, at different offsets.

The following fragment shader uses a uniform named u_post to determine the amount of blur to provide for the particular rendering frame. Each frame uploads a new value to u_post in JavaScript.

More Efficent Options

It would be more efficient to divide u_post by 100 in JavaScript. The fragment shader runs multiple times per frame, but JavaScript only runs once per frame.

For efficiency first process as much as possible in JavaScript. Second process as much as possible in the vertex shader. Third process what's needed in the fragment shader. Fragment shaders run more often than vertex shaders. Vertex shaders run more often than JavaScript animation frames, unless there's just one vertex to render. This particular example could improve but it does work.

Notice each call to texture2D() uses a different texel offset to sample color from the same texture. In this case, the texture comes from the frame buffer.

precision mediump float;

uniform sampler2D u_sampler0;
varying vec2 v_tex_coord0;
uniform float u_post;
        
void main(void) {

float blur = u_post/100.0;

vec4 color0 = texture2D(
u_sampler0, 
 vec2(v_tex_coord0.s - blur, v_tex_coord0.t - blur)
);
color0.a = 0.75;

vec4 color1 = texture2D(
u_sampler0, 
 vec2(v_tex_coord0.s + blur, v_tex_coord0.t + blur)
);
color1.a = 0.75;

vec4 color2 = texture2D(u_sampler0, 
 vec2(v_tex_coord0.s + blur, v_tex_coord0.t - blur)
);
color2.a = 0.75;

vec4 color3 = texture2D(u_sampler0, 
 vec2(v_tex_coord0.s - blur, v_tex_coord0.t + blur)
);
color3.a = 0.75;

vec4 color4 = texture2D(
 u_sampler0, 
 v_tex_coord0
);

gl_FragColor = (
 color0 + 
 color1 + 
 color2 + 
 color3 + 
 color4)/5.0;
}

Summary

This article discussed a few details regarding post processing with WebGL. Most of Seven Thunder Software's WebGL Post Processing examples render to a frame or depth buffer, switch shader programs, process the rendered texture with new shaders, then display the result to the canvas. The first program prepares the basic rendered frame. The second program prepares post processing.

This tutorial provided information regarding rendering to a framebuffer and rendering to a depth texture. The end of the article includes the entire blur post processing vertex and fragment shaders used in the Cube Blurs with Post Processing example. The blur post processing shaders execute after the basic rendering shaders.

See more WebGL examples below. Have fun and love learning!

3D Graphics Run with WebGL! Read the E-Book

WebGL Scenes: Responsive Web Design

Copyright © 2015 Seven Thunder Software. All Rights Reserved.