Saturday, March 16, 2013

Using Shaders and Selective Glow Effects in Three.js

When I first started working with Three.js, in addition to the awe-inspiring collection of examples from Mr.doob, I was also dazzled by the "glow examples" of Thibaut Despoulain: the glowing Tron disk, the animated glowing Tron disk (with accompanying blog post), the Tron disk with particles, and more recently, the volumetric light approximation (with accompanying blog post).  At the time, I didn't know anything about shaders (although there were some excellent tutorials at Aerotwist, part 1 and part 2).

Recently, I learned about the Interactive 3D Graphics course being offered (for free!) at Udacity, that teaches participants all about Three.js. This course is taught by Eric Haines (Google+, Blog), who has written the excellent book Real-Time Rendering.  Eric stumbled across my own collection of introductory examples for Three.js, and recommended a really great article about an introduction to pixel shaders in Three.js  (a.k.a. fragment shaders) by Felix Turner at Airtight Interactive. This motivated me to do two things: (1) sign up for the Udacity course (which is really excellent, and highly recommended!), and (2) to update my collection of Three.js examples for the latest release of Three.js (version 56 at the time of writing).

While updating, I noticed that a number of new classes had been created for shaders and post-processing effects, and so I spent a lot of time looking through source code and examples, and eventually created two new demos of my own: a minimal example of using a Three.js pixel shader, and a shader explorer, which lets you see the results of applying a variety of the Three.js shaders (sepia, vignette, dot screen, bloom, and blur) and the effects of changing the values of their parameters.

Inspired, I revisited the glowing Tron disk examples, hoping to use the BlendShader.js now included as part of the Three.js github repo.  Alas, the search term "THREE.BlendShader" is almost a Googlewhack, with only two results appearing (at the time of writing, which will hopefully change soon); using these links as a starting point, I found a nice example of using the BlendShader to create a neat motion blur effect on a spinning cube.

Next came some experimentation to implement the approach described by Thibaut above, which requires the creation of two scenes to achieve the glow effect.  The first is the "base scene"; the second contains copies of the base scene objects with either bright textures (for the objects that will glow) or black textures (for nonglowing objects, so that they will block the glowing effect properly in the combined scene). The secondary scene is blurred and then blended with the base scene.  However, BlendShader.js creates a linear combination of the pixels from each image, and this implementation of the glow effect requires additive blending.  So I wrote AdditiveBlendShader.js, which is one of the simplest shaders to write, as you are literally adding together the two color values at the pixels of the two textures you are combining.  The result (with thoroughly commented code) can be viewed here:

Hopefully other Three.js enthusiasts will find this example helpful!