Physically Plausible Shading
Shader Pipeline Methods and Data Accumulation


return to main index



Introduction

Over and above the complications of writing RSL2 shaders - compared to writing classic shaders - coding a physically plausible shader presents a significant challenge because of the need to understand the order in which shader methods are called and how data is accumulated as a result of a material, light sources and Pixar's "integrators" working cooperatively to form a shading pipeline.

To illustrate the interactions that occur between a material and a light source two of the sample shaders provided by Pixar,

    Pixar/RenderManProServer-18.0/lib/rsl/shaders/helloAS.sl
    Pixar/RenderManProServer-18.0/lib/rsl/shaders/plausibleArealight.sl

were "peppered" with printf() statements. For example,


public void diffuselighting(output color Ci, Oi) {
    printf("material: diffuselighting() start\n");
    printf("material: diffuselighting() calling directlighting()\n");
    Ci += directlighting(this, getlights());
    if(diffuseSamples > 0) {
        printf("material: diffuselighting() calling indirectspecular()\n");
        Ci += m_diffuse->m_diffColor * indirectdiffuse(P, m_shadingCtx->m_Nn, 
                                                               diffuseSamples);
        }
    printf("material: diffuselighting() finish\n");
    }

The helloAS.sl (uses the Ashikhmin/Shirley shading model) and plausibleArealight.sl source files were copied to Cutter's rsl source directory and re-compiled.


Figure 1
Locations where Cutter expects to find rsl source files and
where it directs Pixar's shader compiler to put .slo files.


The rib file shown below was used to generate the output of the printf() statements. To reduce the quantity of text the material shader was assigned to a single point - it is shaded only once when the hider is set to "hidden".


Listing 1 (dataflow.rib)


Option "searchpath" "shader"  "@:../shaders"
Option "searchpath" "texture" "@:../textures"
#Hider "raytrace"
Hider "hidden"

Display "point_dataflow" "it" "rgba"
Format 640 360 1
Projection "perspective" "fov" 20
ShadingRate 1
  
Translate  0 0 3
Rotate 0 1 0 0
Rotate 0   0 1 0
Scale 1 1 -1
WorldBegin
    Attribute "visibility" "int specular" [1] "int diffuse" [1] 
              "int transmission" [1]
    TransformBegin
        Translate 0 0 2
        Scale 4 4 1
        LightSource "plausibleArealight" 3
                "float intensity" 2
                "color lightcolor" [1.0 1.0 1.0]
                "float minSamples" 4
                "float maxSamples" 8
                "float sides" 2
    TransformEnd
    AttributeBegin
        Surface "helloAS"   "float specularSamples" 4
                            "float diffuseSamples" 8
                            "float specularGain" 0.6
        ShadingInterpolation "constant"
        Points "P" [0 0 0] "constantwidth" [0.2]
    AttributeEnd
WorldEnd

Because the edited versions of helloAS.sl and plausibleArealight.sl also printed the values of the data contained in the array of __radiancesample structs the "min" and "max" sampling of light were reduced to 4 and 8. Likewise, the rib values of "specularSamples" and "diffuseSamples" were reduced.


private void printRadianceSamples(string label; __radiancesample samples[]) {
    uniform float i;
    if(m_print == 0)
        return;    
    printf("-----------%s------------\n", label);
    for(i = 0; i < arraylength(samples); i += 1) {
        printf("direction %1.3p\n", samples[i]->direction);
        printf("radiance %c\n", samples[i]->radiance);
        printf("materialResponse %c\n", samples[i]->materialResponse);
        printf("accumulatedMaterialResponse %c\n", samples[i]->accumulatedMaterialResponse);
        }
    printf("----------------------------\n");
    }


The Calling of Pipeline Methods

The pipeline methods implemented by plausibleArealight.sl are:


    public void light(output vector L; output color Cl)
    public void construct()
    public void begin()
    public void prelighting(output color Ci, Oi)
    public void generateSamples(string integrationdomain; output __radiancesample rsamples [])
    public void evaluateSamples(string integrationdomain; output __radiancesample rsamples[])

The pipeline methods implemented by helloAS.sl are:


    public void construct()
    public void begin()
    public void prelighting(output color Ci, Oi);
    public void lighting(output color Ci, Oi)
    public void diffuselighting(output color Ci, Oi)
    public void specularlighting(output color Ci, Oi)
    public void evaluateSamples(string distribution; output __radiancesample samples[])
    public void generateSamples(string type; output __radiancesample samples[])

The output of the printf() statements is shown below. The pipeline methods of helloAS.sl and plausibleArealight.sl are shown in blue while the integrator, and other RSL functions, that are called by the pipeline methods are shown in red.

RENDER BEGIN________
light::construct()
material::construct()
material::begin()
material::prelighting()

material::lighting() start
material::lighting()   -calling directlighting()
   [directlighting() requests a list of samples from the material. Each
    sample represents a direction (vector). Later in the pipeline the light
    shader will calculate how much of its overall illumination contributes
    to the color of the light hitting a surface for each sample direction]
    material::generateSamples() start
    material::generateSamples()   -calling genSpecularSamps()
    material::generateSamples()   -genSpecularSamps() generated 4 samples
    material::generateSamples() finish

   [directlighting() next requests a list of samples from the light. Each
    sample represents a direction and a (radiance) color]
    light::begin()
    light::generateSamples() start
    light::generateSamples()   -generated 8 samples
    light::generateSamples() finish

   [directlighting() next requests a list of samples from the material. 
    Each sample represents the diffuse (materialResponse) color]
    material::evaluateSamples() start
    material::evaluateSamples()   -calling evalDiffuseSamps()
    material::evaluateSamples()   -8 samples were evaluated
    material::evaluateSamples() finish

   [directlighting() finally requests another list of samples from the 
    material. Each sample represents the combination of the diffuse 
    and specular contributions (accumulatedMaterialResponse) color]
    material::evaluateSamples() start
    material::evaluateSamples()   -calling evalSpecularSamps()
    material::evaluateSamples()   -8 samples were evaluated
    material::evaluateSamples() finish

    light::shadowSamples() start
    light::shadowSamples() finish
    light::evaluateSamples() start
    light::evaluateSamples() finish
    light::shadowSamples() start
    light::shadowSamples() finish
    material::lighting() directlighting() generated 4 samples
    material::lighting()   -calling indirectspecular()()
    material::lighting()   -indirectspecular() generated 4 samples
    material::lighting()   -calling indirectdiffuse()
material::lighting() finish
RENDER END__________


Mixing Pipeline Methods and Hider Options

The dataflow.rib file was rendered alternatively using,
    Hider "hidden"
and,
    Hider "raytrace"
The following tables indicate which pipeline methods are used when different hiders are specified in the rib file. They also show what happens when some pipeline methods are absent from helloAS.sl. The values indicate the number of times each method was called. The large number of (camera) ray hits for the "raytrace" hider is caused by the large size of the single RiPoints gprim. RiPoints usually represent particles and as such are normally much smaller than 0.2.

               lighting()   diffuselighting()   specularlighting()
    "hidden"      1               0                    0
    "raytrace"    0               1                    86

                            diffuselighting()   specularlighting()
    "hidden"                      1                    1
    "raytrace"                    1                    86

               lighting()   diffuselighting()   
    "hidden"      1               0                 
    "raytrace"    87              1                  

               lighting()                       specularlighting()
    "hidden"      1                                    0
    "raytrace"    87                                   0


Pipeline Methods and the Accumulation of Data

The helloAS shader uses instances of several structs (aka databases) in which it stores and manipulates data. This section looks at the use of the __radiancesample struct defined in,
    RPS/lib/rsl/include/stdrsl/RadianceSample.h

As a surface, in our case the single point specified in dataflow.rib, is rendered the plausibleArealight and helloAS shaders work cooperatively to accumalate data that eventually enables helloAS to assign a final opacity and color to the surface. By printing the values contained in the array of instances of the __radiancesample structs (as references to them are passed between the generateSamples() and evaluateSamples() methods) it is easy to see the accumulation of data. For brevity only the values of the first sample is shown below.

-----------MATERIAL generateSamples------------
    direction 0, 0, 0
    radiance 0, 0, 0
    materialResponse 0, 0, 0
    accumulatedMaterialResponse 0, 0, 0
    4 samples in total

-----------LIGHT generateSamples------------
    direction -0.497, 0.321, -0.806
    radiance 2, 2, 2
    materialResponse 0, 0, 0
    accumulatedMaterialResponse 0, 0, 0
    8 samples in total

-----------MATERIAL evaluateSamples------------
    direction -0.497, 0.321, -0.806121
    radiance 2, 2, 2
    materialResponse 0.0392593, 0.0392593, 0.0392593
    accumulatedMaterialResponse 0, 0, 0
    8 diffuse samples in total

-----------MATERIAL evaluateSamples------------
    direction -0.497085, 0.321053, -0.806121
    radiance 2, 2, 2
    materialResponse 0, 0, 0
    accumulatedMaterialResponse 0.0822628, 0.0822628, 0.0822628
    8 specular samples in total


© 2002- Malcolm Kesson. All rights reserved.