I was just watching the excellent behind-the-scenes material on the Blu-Ray release of the new Star Trek film. J. J. Abrams discussed his choice to use lens flares and fake dirt/moisture elements to enhance the realism of CGI space shots. While he may have gone a little too far in this particular instance, I believe lens effects are a very helpful technique to make CG imagery appear less artificial.
I developed a custom lens flare tool for my Mars Phoenix animation project in 2006. Although lens flares are traditionally a feature of 3D rendering packages, I chose instead to handle them as a 2D compositing element. I implemented a simple “renderer” that draws radial glows and discs based on the projected location of a light source in screen space. My custom pipeline integration package (interp) already had the ability to color each pixel of a 2D layer based on an arbitrary function of x,y position. Using this feature I was able to implement the flare renderer entirely in script, without adding a single line of C++ code to the core interpreter.
The original script takes as input the screen-space location of a single light source (as determined by sampling the baked motion data and transforming it through the camera-to-screen projection). Then it generates a list of radial glows and lens reflection rings, and finally goes down the image line by line, iterating through the list and adding up the contribution from each element.
This system works great for single point light sources like the sun. But today I wanted to push the technique further, to handle many sources and large area glows. I imagine the ultimate lens-effect system would not require you to specify the light source locations numerically; instead you should be able to give it an HDR image and then automatically generate lens flare for each sufficiently bright pixel. This way you could design the flare effect just once, and then apply it to any scenario like multiple small point sources or large glows and trails.
It was not too difficult to implement this within interp. I just had to add one C++ function to scan through an image and return a list of all the non-zero pixels. I used script functions to darken and clamp the source image so only very bright pixels would be identified. Then I instantiated a glow-and-ring lens flare for each pixel in the list.
Here are the results:
I was particularly surprised by the smooth appearance of anamorphic radial glows. In the past I had always made these using a 2D blur filter. But the new flare renders looked a lot better. It’s probably because they use a true radially-symmetric blur, as compared to the blobby Gaussian effect you get with a separable 2D filter.
Note the superior appearance of the horizontal glow bands in the “radial glow” image compared to the glow that was based on a 2D Gaussian blur.
Unfortunately, the renderer slows down from a few seconds per frame to a minute or more when it has to draw more than a handful of flares. Despite interp’s efficient line-by-line SIMD compositing architecture, there is still too much interpreter overhead for this operation. At first I thought about implementing more of the glow-and-ring drawing in C++, which would involve finding efficient bounding boxes for each element and sorting them in screen space. But given that I have RenderMan handy, it would be easier just to translate the necessary operations into a RIB scene and let PRMan draw it.
I will probably use this technique for my next 3D production. It can produce beautiful area glows and will save me the effort of specifying individual flare elements for the sun and specular glints.