|
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
|