Adding an occlusion pass

Air’s surface shaders provide a consistent set of output variables for common rendering passes such as diffuse, specular, reflection, and per-light channels.  Any or all of these values can be saved during a single invocation of the renderer, either to separate image files or as individual layers in a multi-layer OpenEXR file.  What if we need to save an additional pass, such as occlusion, as part of the rendered image set?

If we were rendering each pass with a separate invocation of the renderer, we could use one of Air’s command filtering options to override the surface shader assignments with a shader that computes the desired value, occlusion in this case:

air -surf occlusionpass myscene.rib

However, if we’re trying to record all output values in a single rendering, we need to preserve the existing shader assignments.  There are at least two ways to add additional passes to the output mix without clobbering the existing shaders:

Method 1:

Add an additional output variable with the desired value to the surface shaders.  For shaders built with the Vshade user interface, the VPlastic surface shader can serve as a model.  It includes extra blocks that compute an occlusion value exported as a __occlusion output variable.  In the Vshade source network, the occlusion code and output variables are just two blocks which can easily be copied to other Vshade surface shaders.

Vshade occlusion blocks

For text-based shaders, we can copy-and-paste the necessary code fragments.  First, add two new arbitrary output variables (AOVs):

surface mysurf(...;
   output varying color __occlusion = 0;
   output varying vector __bent_normal = 0;

then include code to compute the occlusion values:

if (raylevel()==0) {
  if ((isshadowray()+isindirectray())==0) {
    float doOcclusion = 0;
    option("display:__occlusion", doOcclusion);

    if (doOcclusion!=0) {
       normal Nf = faceforward(normalize(Normal),I);
       __occlusion = occlusion(Position,Nf,PI*0.5,__bent_normal);
       __bent_normal = vtransform("current","world",__bent_normal);

This code only computes the __occlusion value when it is being saved in an output image.

With this method of computing occlusion, the occlusion quality parameters must be set using the corresponding indirect attributes, which are documented in the Air user manual in the Indirect Lighting section of the Lighting chapter.

Method 2:

Use a volume shader that emits the desired value, and assign the volume shader as the Atmosphere shader for the desired objects.  Atmosphere shaders are normally used for volumetric effects.  For scenes that do not require atmospheric effects, we can use a “fake” atmosphere shader (that does not modify the main output color or opacity) as a container for additional output variables.

As an example, I’ve converted to the massive_occlusionpass surface shader included with Air to a volume shader – massive_occlusion_layer:

The converted shader provides a __occlusion output variable with the occlusion result; the shader does not alter the output color or opacity from the surface shader.

To assign a volume shader as an atmosphere shader, use the Atmosphere RIB statement:

Atmosphere "massive_occlusion_layer"

If you are rendering with Massive, you can assign the above shader to all objects by placing the above text in a small file  and either specifying that file as the rib options file in Massive, or including it on the command line when rendering from a command shell:

air addocc.rib myscene.rib

Although the above discussion focused on occlusion, the same techniques can be used to compute and export any desired value for inclusion in a multi-layer EXR file or other storage format.

This entry was posted in Passes and tagged , , , . Bookmark the permalink.