RenderMan Artist Tools

PhotoRealistic RenderMan
Application Note #26

Soft Shadows in PhotoRealistic RenderMan

August, 1998

Figure 1: Soft Shadows


PhotoRealistic RenderMan has always supported shadow computations through the use of prerendered shadowmaps, which can determine shadowing information for a single light source point. Unfortunately, such shadows have hard boundaries and are characteristic of computer graphics (see figure 2). More realistic shadows come from light sources that aren't a single point; the RenderMan spec defines the use of area lights but they aren't implemented in PRMan.

Shadow computations from area light sources require visibility checks with more than one point on the light source. One way to approximate area shadows is by rendering multiple shadowmaps from various points on the light source, and averaging their shadow contributions. (see figures 3 and 4). If we were able to render an infinite number of shadowmaps, this would converge to the answer we seek. This is infeasible, of course, so we would like a solution which can approximate soft shadows without requiring a large number of shadowmaps.

PRMan 3.8 provides such a solution. The shadow shadeop has a new form which accepts multiple shadowmaps and a specification of the "area light's" shape. Since this only affects shadow computations, you don't get other phenomena associated with area light sources (such as non-point highlights), but the softening of shadows goes a long way to make illumination more realistic.

How to use them

Before you can render an image with soft shadows, you must render shadowmaps which will provide the renderer with visibility information for the occluding objects. While normal shadows require only one shadowmap rendered from the light source's location, soft shadows require multiple shadowmaps from various points on the light source's area. The arrangement of the shadowmaps is up to the user, but in general a few shadowmaps from evenly-spaced points on the light source will produce the best results.

IMPORTANT: There are currently two restrictions which must be followed when rendering these shadow views:

Once you have multiple shadowmaps providing various views of the occluding objects, you can use them in a shadow call to render soft shadows. There are a few differences between the soft-shadow form of shadow and the normal case:

As before, the bias parameter specifies how far to shift samples towards to the light source to prevent self-shadowing of objects.

The samples parameter still specifies how many samples to cast when performing the shadowmap computation. Larger values will produce less noisy output and smoother shadows, but are proportionally more expensive to compute.


Suppose you have rendered the four shadow views from figure 3 into the zfiles "light1.z", "light2.z", "light3.z", and "light4.z". Before you can use these for soft shadows, you must convert these into minmaxshadow files. This can be done in one of two ways:

   txmake -minmaxshadow light1.z light1.shad
   txmake -minmaxshadow light2.z light2.shad
   txmake -minmaxshadow light3.z light3.shad
   txmake -minmaxshadow light4.z light4.shad

Alternately, you can place a MakeShadow call in the RIB stream after each shadow pass. For example, in the RIB that creates "light1.z",

   MakeShadow "light1.z" "light1.shad" "minmax" 1
will create the minmax shadow file "light1.shad".

Next, we must write a shader that will make use of multiple minmax shadow files. Here's an example:

light arealight(color lightcolor = color(1,1,1);
		float intensity = 1;
		string maplist = "";
		float numsamples = 1;
		point Pl1 = point(0, 0, 0);
		point Pl2 = point(0, 1, 0);
		point Pl3 = point(0, 0, 1);
		point Pl4 = point(0, 1, 1);
		float shadowBias = 0.001;
		float gapBias = 0.01) {
    varying float attenuation;

    illuminate ((Pl1+Pl2+Pl3+Pl4)*0.25) {  /* base illumination at average */
                                           /* of light positions           */

    attenuation = shadow(maplist,Ps,"source",Pl1,Pl2,Pl3,Pl4,
              "samples",numsamples, "bias",shadowBias, "gapbias", gapBias);

    Cl = lightcolor * intensity * (1-attenuation);

Note that this shader expects the comma-separated list of shadow views to be passed through the parameter "maplist." To use this shader in a final render, we make a light source in RIB that uses the above shader. For this shader, we pass in the list of views and the four corners of the light source:

LightSource "arealight" 1 "intensity" [1]
	    "maplist" ["light1.shad,light2.shad,light3.shad,light4.shad"],
	    "Pl1" [1.15622 -8.83187 4.78469]
	    "Pl2" [0.191448 -6.11682 8.94722]
	    "Pl3" [5.9265 -7.13679 4.78469]
	    "Pl4" [4.96173 -4.42175 8.94722]
	    "gapBias" [0.03]
	    "shadowBias" [0.3]
	    "numsamples" [36]

Figure 2: Hard shadow

Figure 3: Four shadowmaps

Figure 4: Averaging the results of four shadowmaps


Pixar Animation Studios
(510) 752-3000 (voice)   (510) 752-3151 (fax)
Copyright © 1996- Pixar. All rights reserved.
RenderMan® is a registered trademark of Pixar.