RfM Ribbox
Volumetric Spotlight Effects (Reyes)

Revised Jan 2014
return to main index


This tutorial presents a way of making a Maya spotlight behave as if it were a volumetric light source. Although the method shown here requires a moderate amount of setting up it does not rely on the use of additional Maya geometry such as a cone to achieve its volumetric effect. The TCL proc shown in listing 1 ensures that changes made to the spotlight, such as its cone angle, penumbra angle and target location automatically modify the bounding box parameters of a Volume "cone" statement that the TCL proc inserts into the rib stream.

RMS 18

This tutorial was originally written for RMS 4. With the introduction of RMS 18 the Slim BaseVolume node (see Step 1 below) now generates a shader that uses the physically plausible routines that are part of,
Consequently, for RMS 18, ray tracing must be enabled. Part of "Step 2" and all of "Step 3" should be ignored.

The TCL script given in listing 1 will NOT work directly with a RMSGeoAreaLight. However, this limitation can be easily overcome by using a regular Maya spotlight with its intensity set to zero. To obtain the kind of volumetric lighting shown in figure 1 create a RMSGeoAreaLight and set its "Shape" to "spot".

Figure 1
RMS 4. Deep map size 512, map generation time 4 seconds
intensity 1.5, decay rate "linear", [volumespot]

The technique shown on this page uses a RIB Box to call a TCL proc. The BIB Box is assigned to a custom shading group of a Maya spotlight transform nose (tab). By default the TCL script inserts the following rib statements into the scenes .rlf - RenderMan Look File. The most important line of text is,

    Volume "cone" [-7.008 7.008 -7.008 7.008 -11.44 0] [10 10 10]

The first set of values specify the bounding box of a volume cone. The values are automatically set by the TCL proc and are based on any adjustments the user makes to the Maya spotlight.

    IfBegin "$user:pass_class == 'Final'"
        Attribute "dice" "float minlength" 0.05
        Attribute "dice" "float minlength" 0.1
    IfBegin "$user:pass_class == 'Final' || $user:pass_class == 'Preview'"
        Attribute "dice" "string minlengthspace" "world"
        Volume "cone" [-7.008 7.008 -7.008 7.008 -11.44 0] [10 10 10]
        #Attribute "dice" "string minlengthspace" "world"
        #Volume "cone" [-7.008 7.008 -7.008 7.008 -11.44 0] [10 10 10]

Listing 1 (VolumeUtils.tcl)

proc volumespot {  { voxels 10 } { deep_shd_vis 0} } {
    # Link our local variable tnode (ie. name of the transform
    # node) to the pre-defined variable created by RfM.
    upvar OBJPATH tnode 
    set snode [mel "listRelatives -shapes $tnode"]
    set cone [mel "getAttr $snode.coneAngle"]
    set penum [mel "getAttr $snode.penumbraAngle"]
    set cone [expr $cone + ($penum * 2) + 2.0]
    set angle [expr $cone * 0.75 * 0.01745]
    set zdist [mel "getAttr $snode.centerOfIllumination"]
    # Find the xy bounds
    set xy [expr tan($angle) * $zdist]
    set cmnt "#"
    if {$deep_shd_vis} {
        set cmnt ""
    # We assume 0.05 is a reasonable minlength for the final render pass
    set rib    "IfBegin \"\$user:pass_class == 'Final'\"\n"
    append rib  "    Attribute \"dice\" \"float minlength\" 0.05\n"
    append rib "Else\n"
    append rib  "    Attribute \"dice\" \"float minlength\" 0.1\n"
    append rib "IfEnd\n"
    set dice_space "Attribute \"dice\" \"string minlengthspace\" \"world\""
    set geo_volume "Volume \"cone\" \[-$xy $xy -$xy $xy -$zdist 0\] \[$voxels $voxels $voxels\]"
    append rib "IfBegin \"\$user:pass_class == 'Final' || \$user:pass_class == 'Preview'\"\n"
    append rib "    $dice_space\n"
    append rib "    $geo_volume\n"
    append rib "Else\n"
    append rib "    $cmnt$dice_space\n"
    append rib "    $cmnt$geo_volume\n"
    append rib "IfEnd"
    return $rib

Sourcing TCL Scripts

If the suggestions in the tutorial "RfM: Customizing" have been implemented by the reader then getting RenderMan Studio to source a TCL script(s) is relatively easy. Create a directory named RfM_tcl in the maya/projects folder and save the TCL script.

Save the next script, listing 2, in the maya/projects/RfM_ini directory.

Listing 2 (RfM.ini)

set tclDirectory FULL_PATH_TO_USERS/maya/projects/RfM_tcl
# Load the customed TCL procs for use in Ribboxes
LoadExtension tcl [file join $tclDirectory VolumeUtils.tcl]
# If we see this message in Maya's script window we know
# this initialization file has been read successfully.
::RfM::LogMsg INFO "Custom RfM.ini loaded"

Work Flow

Step 1

Open the Slim shader editor and make an instance of the BaseVolume node.

Figure 2

The "Volume Density" parameter should be lowered to 0.1. Choose "Add to Scene" from the popup menu (right mouse button). This will create VolumeDensitySG shading group in HyperShade. Despite its name the BaseVolume shader is NOT a traditional volume shader. It is a surface shader that performs full spherical lighting calculations as it "steps" its way through a volume.

Figure 3

Step 2

Create spotlight and set its position and the location of its "target".
Ignore the remainder of step 2 when using RMS 18.
Choose the shape tab, for example, "spotLightShape1", and from the Attributes menu select,
    RenderMan->Add Shadow Attrs
    RenderMan->Add Custom Deep Shadow Map

Step 3

Ignore this step when using RMS 18.
Open the RenderMan Controls window and set the rmanDeepShadowPass to,
    Resolution to 256
    Shading Rate to 4

Step 4

Choose the transform tab, for example "spotLight1", and select,
    Attributes->RenderMan->Add Custom Shading Group
Open the "Extra RenderMan Attributes" panel. Right mouse click in "Custom ShadingGroup" text field and choose BaseVolumeSG.

Figure 4

Step 5

Use the BaseVolumeSG tab and select,
    Attributes->RenderMan->Add RIB Box
Select RIB Box Interpolation TCL.

Figure 5

Figure 6

Step 6

Enter the following text into the Rib Box. Be careful to include both the opening and the closing bracket.
    [volumespot] - RMS 4 and RMS 18.
To calculate deep shadow detail in the volume use,
    [volumespot 10 1] - RMS 4 only.
The first value sets the voxalization of the volume cone, the second values ensures the volumetric cone is also used for the deep shadow pass.

Figure 7
RMS 4. Deep map size 512, map generation time 20 minutes
intensity 150, decay rate "linear", [volumespot 10 1]

Selective Lighting - RMS 4 Only

It is rare to use a volumetric light in isolation. Using additional lights in a scene generally leads to the problem shown in figure 8 where the second light illuminates BOTH the geometry within the volume and the interior of the volume cone itself. Figure 9 shows what is more often required.

Figure 8

Figure 9 - selective lighting

To illustrate how a shader assigned to a volume can selectively respond to some lights the BaseVolume shader generated by Slim will be replaced by a custom surface shader - listing 3.

Listing 3 (volumecloudy.sl)

surface volumecloudy(float  freq = 2.0,
                            Kd = 0.7) 
color c = 0;
float fogFlag = 0;
illuminance(P) {
    // Only use lights with a "fogIntensity" of -1
    if(lightsource("fogIntensity", fogFlag)) {
        if (fogFlag == -1)
            c += Cl;
Oi = float noise(transform("current", "shader", P) * freq);
Ci = Oi * Cs * c * Kd;

The shader should be compiled and it's .slo file moved to the appropriate Maya project directory. Note it is a surface shader and consequently must be imported into HyperShade as a material. Incidently, despite it's name, BaseVolume is NOT a volume shader but is also a surface shader. Unlike most surface shaders both BaseVolume and the custom shader given in listing 3 do not calculate illuminance relative to a surface normal ie.

    illuminance(P,N,PI/2 ) {

but, instead, calculates the full spherical lighting contribution at each point that it samples within a volume ie.

    illuminance(P) {

Also, note the illuminance loop only accumulates a lighting value if, and only if, the lightsource being sampled has a parameter called "fogIntensity" with a value set to -1.0. The technique shown here is somewhat of a "hack" because the preferred way of selectively sampling specific lights is through the use of "categories". However, since there is not a way of tagging Maya lights with category names there is no alternative but to use the less efficient technique of looking for a specific lightsource shader parameter.

Selective Lighting Work Flow

Step 1

Select the transform node of the "volume" spotlight.
Right mouse click in the "Custom ShadingGroup" text field and choose VolumeCloudySG

Figure 10

Step 2

Select the shape node of the "volume" spotlight.
From the Attributes menu choose Manage TCL Expressions.

Figure 11

Step 3

In the TCL Expression Manager choose Fog Intensity.

Figure 12

Step 4

In the Fog Intensity Expr text field enter the following TCL.
    [mel "getAttr $OBJNAME.fogIntensity"]

Figure 13

The TCL expression will force RfM to "expose" the fog intensity as a parameter of the lightsource shader that it generates - otherwise the value of fog intensity is "internal" to the shader.

Step 5

In Light Effects set the value of Fog Intensity to -1.0.

Figure 14

Since all Maya lights either do not have a control called "Fog Intensity", for example directional lights, or they do not expose its value as a lightsource shader parameter they will not contribute to the illumination of a volume primitive.

© 2002- Malcolm Kesson. All rights reserved.