unsharp the depth buffer masking technique

It’s been a while since I worked on an extension to three.js - thje webGL 3D engine developed by Mrdoob - to permit to doing post processing  more easily on the 3D result.

Post processing

This technique is commonly used in video games and motion design to improve the final rendering. For example, Glow, Screen Space Ambient Occlusion (SSOA) or Depth Of Field (DOF) are post process effects. you can also use the post processing to correct the rendering’s color, apply deformation warping or filter the image with pixelation, etc …

To do so, we need to apply shaders and textures and to combine them.

Unsharp Masking the Depth Buffer

The first example using the PP.js extension is the Unsharp Maskingthe Depth Buffer which enhances the perception of the elements of a 3D image. This traitmeent is also very close to the SSOA, but at a lower computational cost. To understand this technique, I suggest you to read this article before: Image enhancement by unsharp masking the depth buffer.

The technique

To achieve this treatment, we need to render the 3D scene (diffuse texture) and the depth buffer. Then we apply a gaussian blur on the depth buffer. The difference between the blurred depth buffer and texture diffuse is called spacial importance.
Properly used, this new texture applied to the diffuse texture will help to highlight the difference in depth and contour of objects.

from left/top to right/bottom: diffuse texture, depth texture, depth texture blured on x, depth texture blured, final result

The effect of blur on the texture depth is performed in two passes (horizontal and vertical).

Implémentation: Three.js and PP.js

The PP.js extension can easily create and apply shaders on those textures.
To boot PP is simple:
PP.init({   renderer: renderer,
   scene: scene,
   camera: camera,
   guiEnabled: true});

PP.debug.init();

PP.addTexture('tDiffuse')
    .addTexture('tDepth')
    .loadShader('blurTriangleX', {radius: 25.0})
    .loadShader('blurTriangleY', {radius: 25.0})
    .loadShader('unsharpMasking', {bias: -6.0});

PP.init() initialising the extension. Then we transmit the Three.js’s infos.
Thanks to PP.addTexture() and PP.loaderShader(), we can create size screen textures easely and load shaders that we will use in the rendering loop.

PP.start();

// Render color
for( i = 0; i < nobjects; i++ ) objects[i].materials = [ material ];

PP.renderScene().toTexture('tDiffuse');
PP.get('unsharpMasking').set('textureIn').toTexture('tDiffuse');

// Render depth

for( i = 0; i < nobjects; i++ ) objects[i].materials = [ material_depth ];

PP.renderScene().toTexture('tDepth');
PP.get('unsharpMasking').set('tDepth').toTexture('tDepth');

// Render blur

PP.get('blurTriangleX').set('textureIn').toTexture('tDepth');
PP.renderShader('blurTriangleX').toTexture('blurTriangleY');
PP.renderShader('blurTriangleY');

// Render Unsharp masking

PP.get('unsharpMasking').set('tBlur').toTexture('blurTriangleY');
PP.renderShader('unsharpMasking').toScene();

In the rendering loop, methods PP.renderScene() and PP.renderShader() comput the effects and transmit to the following effect the result of the effect before.

Conclusion

Thanks to Three.js and PP.js, post processing with multiple shaders is simple. I would later come back on PP.js with new examples. The PP.js extension will be downloadable on GitHub when it is fully operational.
I made two examples using this effect:

You can show/hide the effect and control the uniforms parameters with the control pannel.