RSL
Surface Names & Ray Names


return to main index



Introduction

This tutorial looks at the way that a shader can determine the name of the surface to which it has been assigned and the name of a ray that has caused it to be evaluated.



Identifying the Name of a Surface

A rib file produced by mtor or Rfm Pro would describe the contents of a Maya scene by statements, similiar to those shown below.

    AttributeBegin
        Attribute "identifier" "name" ["nurbsSphere1"]
        Surface ...
        ConcatTransform ...
        object data ...
    AttributeEnd

The Attribute statement, shown in bold text, identifies a surface by the name that was assigned to the geometry in the Maya scene graph. A shader can use the RSL function attribute() to querry this name.

For example, the shader in listing 1 has been "hard-coded" to respond to the names "red" and "blue". Listing 2 is a rib file that applies the shader to two spheres. Figure 1 illustrates the result of using the shader.



Figure 1


Listing 1


surface 
color_by_name(float  Kd = 1)
{
normal  n = normalize(N);
        nf = faceforward(n, I);
color   surfcolor = 1;
string  name;
  
if(attribute("identifier:name", name) == 1)
    {
    if(name == "red")
        surfcolor = color(1,0,0);
    else if(name == "blue")
        surfcolor = color(0,0,1);
    /*printf("%s\n", name);*/
    }
  
color  diffusecolor = Kd * diffuse(nf);
Oi = Os;
Ci = Oi * Cs * surfcolor * diffusecolor;
}


Listing 2


Display "untitled" "framebuffer" "rgb"
Format 427 240 1
Projection "perspective" "fov" 20
ShadingRate 1
   
Translate  0 0 8
Rotate -30 1 0 0
Rotate 0   0 1 0
Scale 1 1 -1
   
WorldBegin
    LightSource "pointlight" 1 "intensity" 50 "from" [1 4 1]
   
    Attribute "visibility" "trace" [1] # <<-- turn on raytracing
    TransformBegin
        Attribute "identifier" "name" ["red"]
        Translate -0.5 0.5 0
        Surface "color_by_name"
        Sphere 0.5 -0.5 0.5 360
    TransformEnd
    TransformBegin
        Attribute "identifier" "name" ["blue"]
        Translate 0.5 0.5 0
        Surface "color_by_name"
        Sphere 0.5 -0.5 0.5 360
    TransformEnd
    TransformBegin
        Attribute "identifier" "name" ["floor"]
        Surface "plastic"
        Scale 4 4 4 
        Polygon "P" [-0.5 0 -0.5  -0.5 0 0.5  
                      0.5 0 0.5  0.5 0 -0.5] 
                "st" [0 0  0 1  1 1  1 0]
    TransformEnd
WorldEnd

The next shader, listing 3, shows another example of using the name of a surface to effect the way it is shaded. The polygonal "floor" has been assigned the ambient occlusion shader developed in the tutorial, "Ambient Occlusion". The "vampire" shader assigned to the two sphere checks the name of the object to which it is attached and if the object name is "vampire" it is given an opacity of zero. Although one of the spheres is invisible its presence continues to effect the floor. In its basic form, ambient occlusion makes no distinction between visible and invisible objects.


Listing 3


surface 
vampire(float Kd = 1)
{
normal  n = normalize(N);
normal  nf = faceforward(n, I);
string  name;
  
if(attribute("identifier:name", name) == 1)
    {
    if(name == "vampire")
        Oi = 0;
    else
        Oi = Os;
    }
  
color   diffusecolor = Kd * diffuse(nf);
Ci = Oi * Cs * diffusecolor;
}

Figure 2 shows the result of using the vampire shader (listing 3). Although the sphere on the right, named "vampire", in the rib file (listing 4), still contributes to occlusion, seen on the polygonal floor, the shader has assigned the sphere full transparency.


vampire
Figure 2


Listing 4


Display "untitled" "framebuffer" "rgb"
Format 427 240 1
Projection "perspective" "fov" 20
ShadingRate 1
   
Translate  0 0 8
Rotate -30 1 0 0
Rotate 0   0 1 0
Scale 1 1 -1
WorldBegin
    LightSource "pointlight" 1 "intensity" 50 "from" [1 4 1]
   
    Attribute "visibility" "trace" [1]
    TransformBegin
        Attribute "identifier" "name" ["untitled"]
        Translate -0.5 0.5 0
        Surface "vampire"
        Sphere 0.5 -0.5 0.5 360
    TransformEnd
    TransformBegin
        Attribute "identifier" "name" ["vampire"]
        Translate 0.5 0.5 0
        Surface "vampire"
        Sphere 0.5 -0.5 0.5 360
    TransformEnd
    TransformBegin
        Attribute "identifier" "name" ["floor"]
        Surface "occlude1"
        Scale 4 4 4 
        Polygon "P" [-0.5 0 -0.5  -0.5 0 0.5  
                      0.5 0 0.5  0.5 0 -0.5] 
                "st" [0 0  0 1  1 1  1 0]
    TransformEnd
WorldEnd


Identifying the Name of a Ray

The two previous examples illustrate the use of the RSL function attribute(). However, it is not only objects that can be identified by a name; rays that probe a scene for the purposes of ray-tracing an image can also be identified by user-defined names.

When a renderer, such as Pixar's prman, is in raytracing mode a shader can use the RSL function gather() to shoot rays into the scene. For detailed information about this function refer to the RenderMan Interface Specification (draft) - "Section 13 - Language Constructs". Cutter provides a quick way of getting to this document ie.



Figure 3


Gather rays can be tagged with a label/name. Almost any label/name can be assigned to ray providing the name is not camera, light, specular, diffuse or transmission - these are reserved names.

When a gather ray strikes a surface, the shader that is attached to that surface may, under certain circumstances, be evaulated. As we shall see, a shader that is triggered in this way performs its calculations, not for the purpose of coloring a surface, but solely for the purpose of providing the gather ray with information that the ray can "return" to the shader that fired it.

Because gather rays can be labelled, shaders can querry the label or name of the ray that caused them to be evaluated or invoked. Details about these querry methods can be found in the draft RI Spec doc "Section 15 - Built-in Functions". There are a couple of very important points to bear in mind when using ray labels/names.

Firstly, the shader attached to the surface that is struck by a (gather) ray will only be invoked if the gather function requests a value that only the shader can calculate. For example, Oi rather than Os.

Secondly, when a shader is invoked, as a result of the surface to which it is attached being struck by a (gather) ray, the values it calculates, say Oi and Ci in the case of a surface shader, will not be used to colorize the surface. This means that we cannot effect the appearance of an object on the basis of the names of the rays hitting its surface.

Ok, its getting confusing, so lets imagine a highly contrived example where we can see ray-labelling in action. We will implement an occlusion shader that ignores any object named "stealth". For our wacky-effects occlusion shader to perform its magic it will need to work in conjunction with a surface shader whose main purpose is to feed the occlusion shader with information that will enable it to decide if it should ignore a particular surface. Lets call the occlusion shader wacky_occlusion (listing 5) and the other shader stealth (listing 6).


Listing 5


surface 
wacky_occlusion(float  samples = 32)
{
normal  n = normalize(N),
        nf = faceforward(n, I);
float   hit = 1,
        hits = 0;
  
gather("illuminance", P, nf, PI/2, samples,
       "label", "wacky_ray",
       "surface:doOcclude", hit,
       "distribution","cosine")
       {
       if(hit)
            hits += 1;
       }
/* find the average occlusion factor */
float average = hits / samples;
   
Ci = (1 - average) * Cs;
Oi = 1;
}


Listing 6


surface 
stealth( float Kd = 1;
  output varying float doOcclude = 0 )
{
normal  n = normalize(N);
normal  nf = faceforward(n, I);
string  objname = "";
string  rayname = "";
  
attribute("identifier:name", objname);
  
/* Get the name of the ray that hit our surface */
rayinfo("label", rayname);
  
/* Does our surface name match "do_not_occlude" ? */ 
float found = match("do_not_occlude", objname);
  
/* Ok, we've been asked by a gather ray to set up
   a value for the "doOcclude" output variable */
if(found == 1 && rayname == "wacky_ray")
    doOcclude = 1;
        
/* ...we have been hit by some other kind of ray,
   probably a ray coming from the camera */
else /* calculate our opacity and color */
    {
    Oi = Os;
    Ci = Oi * Cs * Kd * diffuse(nf);
    }
}

Here is what each shader does.
stealth

  • use the RSL attribute() function to get the name of the surface to which we have been assigned
  • decide if we have been hit by a gather ray that originated from the wacky_occlusion shader
  • if we have been hit by a gather ray, assign a value to a special variable that will be gathered by the wacky_occlusion shader.
  • if we have been hit by anything other than a wacky_occlusion gather ray we'll colorize our surface in the standard way.

wacky_occlusion

  • use the RSL gather() function to shoot rays
  • name our rays so that they can be identified by the stealth shader
  • for each gather ray that strikes a surface check to see if we have been able to gather the special variable known only to the stealth shader.
  • if we've gathered the stealth variable as a result of a ray hit simply ignore the hit - effectively ignoring the surface that was struck.

Figure 4 shows the effect of applying these two shaders.



Figure 4




© 2002- Malcolm Kesson. All rights reserved.