Packing Lighting Data into RGB Channels

Most existing renderers process Red, Green and Blue channels independently. While this limits the representation of certain optical phenomena (especially those in the domain of physical rather than geometric optics), it provides some advantages as well. For one, this feature allows encoding lighting information from several sources into a single image separately, which we are going to look at in this article.

Advanced masking

For the first example let's consider rendering masks which we have discussed the last time. The image above introduces a bit more advanced scenario when an object is present in reflections and refractions as well, so we want the mask to include those areas also, but at the the same time to provide a separate control over them.

While the most proper approach might be to render two passes (one masking the object itself and another one with primary visibility off – an object only visible in reflections/refractions), there is a way to pack this data into a single render element if desired. Just set the reflection filter color to pure Red (1,0,0), refraction color to Green (0,1,0) and make sure that an object being masked is present in all these channels plus in one extra (meaning it's white in our case). To isolate the mask for a reflection or refraction, we now only need to subtract the Blue channel (in which nothing gets neither reflected nor refracted by our design) from Red or Green respectively.

A custom matte pass allowing isolation of an object in reflections and refractions as well.

As usually when dealing with mask manipulations, special care should be taken to avoid edge corruptions, and this method might not be the optimal for softer (e.g. motion blurred) contours.

Isolating lights

Another situation could require to isolate an input of a particular light source into the scene, including its impact on global illumination. Again, it's great (and typically way faster) when your rendering software provides per-light output of the elements, however this is not always an option.

But taking advantage of the separate RGB processing, as soon as we color each source with a pure Red, Green or Blue, it's impact will be contained within the corresponding channel completely and never “spill out” to another one. Yet the light will preserve the complete functionality including GI and caustics. Of course, all surfaces should be desaturated for this pass (otherwise an initially red object might not react with the light represented in another channel for example).

The resulting data can be used in compositing as a mask or an overlay to correct/manipulate the individual input of each light into the beauty render, for instance to adjust the key/fill lights balance.

For this scene I had only two light sources (encoded in Red and Green), so in a similar fashion I have added an ambient occlusion light into the otherwise empty Blue channel. Ambient occlusion deserves a separate article on its own as it has numerous applications in compositing and is a very powerful look adjustment vehicle. Depending on the software, AO could be implemented as a surface shader, still it can fit into a single channel and be encoded in one custom shader together with some other useful data like UV coordinates or the aforementioned masks.

This weirdly colored additional render contains separate impact of each of two scene lights within Red and Green channels, while Blue stores ambient occlusion for diffuse objects

Saving on volumes

The described technique becomes most powerful when applied to volumes. Volumetric objects usually take considerable time to render and are often intended to be used as elements later (which implies they should come in multiple versions). By lighting a volume with 3 different lights of pure Red, Green and Blue colors we can get 3 monochromatic images with different light directions in a single render.

To have a clearer picture while setting up those lights, it is handy to tune them one at a time in white color and with others off. Enabling all three simultaneously and assigning channel-specific colors can be done right before the final render – the result for each channel should match the white preview of a corresponding source automatically.

Three lights stored in a single RGB image

The trick now is to manipulate and combine them into the final picture. All sorts of color corrections and compositing techniques can be used here, but I find gradient mapping to be especially powerful. Coming under various names but available almost universally in image editors of all sorts, it is a tool allowing to remap a monochromatic range of an input into an arbitrary color gradient, thus “colorizing” a black-and-white image.

Source image before gradient mapping
Gradient-mapped result

Summing it up

The next cool thing is that the light is naturally additive, and the results of these custom mappings for different channels can be added together with varying intensities, resulting in multiple lighting versions for the same image.

The qualities of each initial RGB light can be changed drastically through manipulating the gamma, ranges, contrast and intensity of each channel (all can be achieved adjusting the target gradient actually). This also means that light directions with wider coverage should be preferred at the render stage to provide more flexibility for these adjustments.

More results from the same three source channels

On a more general note, this 3-lights technique allows for simulating something like the Normals output variable for volumes. And on the other hand, the rendered Normals pass (which anatomy we are going to discuss the next time) can be used for similar lighting manipulations with surfaces.

The main goal of the provided examples was to illustrate a way of thinking – the possibilities are quite endless in fact.