Prev | Next



Area Light and Area Shadowing Support

April, 2011 (Revised: October, 2011)

Introduction

PRMan 16 includes improved support for area shadowing. The new areashadow() function in RSL allows querying of the shadowing from a number of sampled locations on an area light. It supports both ray tracing and deep shadows generated using the areashadow display mode for the deepshad driver.

Users should note that there are correlating techniques described in the Physically Plausible Shading in RSL application note that may be preferred. Users should also consult the Importance of Importance application note for further discussion of ray tracing optimizations.


areashadow Deep Shadows

PRMan 16 includes a new deep shadow output mode that can record the opacity of multiple objects, including those hidden behind completely opaque objects. This new deep shadow map type can be used with the areashadow() shadeop to compute shadowing. Because it is a map, the technique can be used to compute shadowing without the need for traceable geometry. As such it may be appropriate for geometric types that are traditionally memory intensive, such as hair.

To generate a traceable deep shadow map, the areashadow display mode is used with the deepshad driver. The number of opaque objects to record into the map may be specified with the othreshold option, which should be set to a number greater than one to record geometry that is obscured from the point of view of the camera. Nearer geometry will have priority.

Unlike traditional shadow queries, which can only determine visibility from a texel of the map down its 'z' direction, traceable deep shadows can answer the visibility over a broader range of directions. This makes it possible for them to compute shadowing from a selection of positions (such as the samples of an area light), while only using a single map.

It is still important to disable culling of backfacing geometry and hidden geometry in order for the map to contain sufficient information, A typical RIB fragment might look like this:

Option "limits" "color othreshold" [9 9 9]
    # set how many opaque layers deep to record in the map
Option "limits" "float deepshadowerror" [0.01]
    # specify deep shadow compression metric
Display "null" "null" "z"
Display "+Shadow.tdsm" "deepshad" "areashadow"
    # specify the output file using the areashadow method
Attribute "cull" "int backfacing" [0] "int hidden" [0]
    # ensure all geometry is recorded in the map

Note that these new area shadow maps can be viewed with either the dsview or sho utility.


The areashadow shadeop

areashadow(), in its simplest form, computes the ability of light to flow between a specified point and a selection of points on an area light. This returns the average transmission over all the implied rays:

varying color areashadow ( string mapname; filterregion from;
                                 varying point tos[]  [parameterlist]  )

By supplying a filterregion rather than a point to specify the surface area over which you want to compute shadowing, PRMan can antialias the results. Supposing the resizable array tos contains points you have chosen on an arealight, and mapname is a valid tracable deep shadow map, the average transmission will be computed.

trans = areashadow(mapname,from,tos);

Ray-traced results may be computed by passing "raytrace",1 on the parameter list. If the map name is also not empty, tracing will be performed in addition to the map query. For example:

trans = areashadow(mapname,from,tos,"raytrace",1);

will query both the map and transmission visible geometry, whereas:

trans = areashadow("",from,tos,"raytrace",1);

will perform ray tracing only.

When maps and ray tracing are used together, a map will be queried first. If this misses, or produces only partial shadow, it will continue on to try ray tracing. Users should be aware that the result is cumulative and the same geometry may get double-counted. For this reason, you should consider using the trace "subset" parameter to exclude geometry in the shadow maps from also contributing to the ray tracing results.

It should be noted that areashadow() is considerably more efficient than calling transmission in a loop, and should be used wherever possible.


Automatic Bias Computation

Self-shadowing

As of PRMan 16.1, the areashadow() shadeop can automatically estimate an appropriately tuned bias for deep shadow maps in many situations. This estimate takes into account the map resolution and either map camera scale (for orthogonal shadow maps) or map camera field of view plus the distance of the from point from the shadow map camera (for perspective shadow maps). This is particularly useful in the case where multiple shadow maps are used. As the bias is estimated separately for each ray, it will take into account the particular map that was selected.

The automatic bias is controlled through the mapbias parameter, which scales the estimate. The default of 1 should be appropriate in most cases and will try to produce a bias small enough to preserve contact shadows while still being large enough to avoid self-shadowing artifacts. Occasionally, larger values such as 1.2 may be useful. Setting mapbias to 0 effectively disables the automatic bias.

Note that mapbias does not override the effect of the traditional bias parameter. If both are used, they will be applied in conjunction with each other. For this reason, you may wish to leave bias at 0 if you are not also using ray tracing in the same call.

Light Leaks

A related scale factor, mapbias2, can be used to help control light leaks when tracing rays through shadow maps. In some cases geometry that is seen obliquely from the shadow map camera may have gaps in 'z' between adjacent map texels, which could allow a ray to skip through without receiving the appropriate amount of shadowing. The closer a ray is to parallel with the shadow map camera's image plane, the worse it will suffer from this effect.

To mitigate this, mapbias2 expands the interval in 'z' examined at each texel by areashadow(). This wider filtering in the depth helps with rays that should be fully blocked but only register as partially blocked. Typical values are 1 to 5, with 5 being the default and 0 disabling it. Note that larger values may overly darken penumbras and the shadows of transparent or fine geometry.


More Complex Usage

Often an average transmission is not particularly useful. Far more helpful is the ability to query the transmission of each individual ray implied by the surface/volume position from and the set of area light samples in to. This allows proper integration with a brdf.

areashadow() can do this by supplying additional output parameters. For example:

color perRayTrans[];
color perRayLs[];
float perRayLMags[];

uniform float tolen = arraylength(tos);
resize(perRayTrans,tolen);
resize(perRayLs,tolen);
resize(perRayLMags,tolen);
areashdow(mapname,filteregion(P),tos,perRayTrans,perRayLs,perRayLMag,...)

This returns the transmissivity along each ray, as well as the L vector implied by it and its length. Note that the L vectors are already unit length and the magnitude of each individual L prior to normalization is returned; this is helpful and avoids having to normalize L, which is typically done prior to its use.


Helpers in stdrsl

PRMan 16 also introduces a set of helpful code headers provided to make things simpler for shader authoring. The header "stdrsl/AreaSampler.h" provides helper functions that sample various types of standard area light shapes. For example:

stdrsl_AreaSample areasampler;
areasampler->sampleDisk(radius,nsamples);

will distribute stratified samples into the sampler's areasamples member; thus areasampler->areasamples can be passed directly to areashadow()'s tos parameter.

Support for disks, sampling of a line, rectangle, sphere, and cylinder is included.


Structs available in stdrsl

stdrsl_AreaSampler

The area sampler struct provides useful methods for distributing stratified samples on various types of area light.

The sample member functions provided are:

 public uniform float sampleDisk (uniform float radius; uniform float samples) ;
 public uniform float sampleLineaar (uniform point point1; uniform point point2; uniform float samples;) ;
 public uniform float sampleRectangle (uniform float xsize; uniform float ysize; uniform float samples;) ;
 public uniform float sampleCylinder (uniform float zmax; uniform float zmin; uniform float radius; uniform float samples;) ;
 public uniform float sampleSphere (uniform float zmin; uniform float zmax; uniform float radius; uniform float samples;) ;

Two members are available after one of the sample methods has been called:

varying point areasamples[];    // The positions on the light
varying normal areaNs[];        // Normals corresponding to the positions above

Note that the normal may be 0 for linear lights. It is also important to note that the number of samples may differ slightly from the requested number due to the stratification method.


Example

Here's a scene containing hair and complex geometry:

images/figures.areashadow/areashadow.png

Multiple Shadow Maps

The areashadow() shadeop accepts an array of deep shadow map names. This can be used to select the most efficient map that most accurately can answer the shadowing of a given ray.

While the area shadow maps do not need to be strictly positioned at each light source as with traditional shadow maps, the areashadow() shadeop will produce faster and more accurate results the more closely aligned one of the available maps is.

Our current heuristic simply chooses the map with the view direction that has the smallest angle difference from the ray direction. This ignores the shadow camera position, map resolution, ray origin, and other factors. As a result, all shadow maps used together in a single areashadow() call should include the same geometry at roughly the same resolution.

The following shows a scene and the two cameras from which areashadow-type deep shadows are rendered (though typically more would be needed). An example soft shadow is shown below.

images/figures.areashadow/twoshadows.png images/figures.areashadow/twoshadows_render.png

Rendering Hair

At small, subpixel scales there can be ambiguity over whether a change in the transmission through an area shadow map pixel is due to an opaque object that partially covers the pixel or a transparent object that fully covers the pixel. For areashadow(), we assume coverage. This allows us to preserve antialiasing at the edges of shadows and minimize light leaks through the interior regions. However, the tradeoff for this is that dense, subpixel-sized, opaque geometry that overlaps a pixel at different pixel samples may be confused for being parts of a larger surface. This can produce aliasing and noisy acne in the shadowed areas due to over-occlusion.

To avoid this problem, we recommend that hairs be at least a pixel wide in the shadow map and be made partially transparent to compensate. Effectively, this is a form of pre-filtering for the area shadow map. We support a technique that can apply this adjustment automatically:

Option "hair" "float minwidth" [1.0]

The value given is a minimum width in pixels. Hair-diced RiCurves and RiPoints that project to a smaller size than this will be widened and reduced in opacity proportionately. We have found 1 to 1.5 to be useful values for area shadow map passes. The minwidth option may also be used for beauty passes but normally will have a lower value. Beware that this option will increase memory use by producing more visible points.

We also recommend that round curves and sigma hiding be off during shadow map passes. The round curves setting will bias the hairs towards the shadow map camera, which may affect self shadowing when rays are traced in other directions for the beauty pass. While the sigma buffer can reduce memory use and help to antialias small geometry for beauty passes, it does so by collapsing samples in depth. This may result in detail missing from the shadow map. It is important to note that sigma should be off on the hider line, not just in the attribute, otherwise widening will not occur.

In short, we suggest the following as typical settings for hair in area shadow maps:

Option "hair" "float minwidth" [1.0]
Hider "stochastic" "int sigma" [0]
Attribute "dice" "hair" [1] "int roundcurve" [0]

The following pair of images shows an example of some thin hairs without and with minwidth set to 1.0 in the shadow pass, respectively:

images/figures.areashadow/hair_notwidened.png images/figures.areashadow/hair_widened.png

Prev | Next


Pixar Animation Studios
Copyright© Pixar. All rights reserved.
Pixar® and RenderMan® are registered trademarks of Pixar.
All other trademarks are the properties of their respective holders.