Short Contents

7.3 Ray Tracing

3Delight implements a number of ray tracing capabilities to render effects such as reflections, refractions, ray probes, transmission probes and soft shadows. Many of the standard SL shadeops have been extended to fully take advantage of 3Delight's ray tracing features. In a summary:

trace()
A function to perform multiple-importance sampling operation in order to sample BRDFs and environments. This is the function to use to render physically-plausible materials. The calling syntax is described in trace shadeop.
transmission()
A function to render ray traced shadows. The calling syntax is described in transmission shadeop.
occlusion()
A shadeop to render ambient occlusion(45) effects. This will intersect with objects visible to transmission rays. The calling syntax is described in occlusion shadeop.
indirectdiffuse()
A shadeop to render color bleeding effects. This will intersect objects visible to reflection rays. Can be used to implement the final gathering algorithm when combined with photon maps, as explained in Final Gathering. The calling syntax is described in indirectdiffuse shadeop. In practice, this function is deprecated in favor of the much more advanced trace().
gather()
A general construct to sample the scene using ray tracing and to sample HDR images. This will interesect objects visible to reflection rays. The calling syntax is described in gather shadeop.

7.3.1 Reflections and Refractions

Using the trace() shadeop is the simplest way to compute reflections and refractions although existing shaders need to be modified. Shaders that use environment() can perform ray tracing without any code modifications by providing "raytrace" as the environment map name. A shader that can be enhanced in this way is the standard `shinymetal'. Note that geometry does not appear in ray tracing unless it is visible to the right type of rays. For environment(), this is accomplished using:

Attribute "visibility" "int specular" [1]

When computing reflection or refractions on certain kind of surfaces (such as glass), it is important to take surface orientation into account to compute the correct index of refraction (eta): rays exiting a surface should invert the index of refraction as shown in the following code snippet:

normal Nn = normalize( N );
vector In = normalize( I );
normal Nf = faceforward( Nn, In );
float eta = 1.3; /* glass */

if( In.Nn < 0 )
{
    /* The ray is exiting from the surface, inverse the relative index of
       refraction */
    eta = 1.0 / eta;
}

/* Compute refracted and reflected ray */
float kr, kt;
vector reflDir, refrDir;
fresnel(In, Nf, eta, kr, kt, reflDir, refrDir);
...
Listing 7.1: Computing the correct index of refraction for surfaces such as glass.

It is often useful to account for an environment map when reflected or refracted rays do not hit anything and this can be achieved using the optional `transmission' parameter available to trace(), as shown in Listing 7.2.

color SampleEnvironment(
    point P;  vector R;  float blur, raysamples;  string envname;)
{
   /* First trace a ray and then composite the environment on top if
      the transmission != 0 */
    color transmission;
    color ray = trace( P, R,
        "samples", raysamples,
        "samplecone", blur,
        "transmission", transmission );

    if( transmission == 0 || envname == "" )
        return ray;

    /* NOTE: should apply correct blur here */
    color env = environment(envname, P, vtransform("world",R) );
    return ray + transmission * env;
}
Listing 7.2: How to combine ray tracing and environment mapping.

7.3.2 Ray Traced Shadows

Before rendering ray traced shadows, one should make sure that shadow casting objects (those that can obstruct light rays) are visible to `transmission' rays. This is accomplished using:

Attribute "visibility" "int transmission" [1](46)

Once the visibility flags are correctly set, one can compute shadows in many ways:

  1. By using transmission(), usually from a light source shader.
  2. By calling shadow() and passing `raytrace' as a shadow map name.
  3. By using the automatic shadows functionality.

Using "automatic" shadows is the simplest way to render shadows since it requires the addition of only one statement in front of a light source declaration (provided that objects are visible to `transmission' rays):

Attribute "light" "shadows" "on"
LightSource "spotlight" 1 ...

Using shadow() with "raytrace" as a shadow map name is another simple way to trace shadows without any shader programming(47); and existing shaders that use shadow maps are directly usable. In this case, user has more control on shadows' shape using the "blur", "bias" and "maxdist" options passed to shadow() (see shadow shadeop). Here is a RIB snippet that uses the standard `spotlight' shader to compute "soft shadows":

LightSource "spotlight" 1 "string shadowmap" ["raytrace"] "float blur" 0.05
…

The specialized transmission() can be used to test the visibility between two given points; it can also be used to render soft shadows. Here is an example light source that uses this shadeop.

light shadowspot (
    float intensity = 1;
    color lightcolor = 1;
    point from = point "shader" (0,0,0);
    point to = point "shader" (0,0,1);
    float coneangle = radians(30);
    float conedeltaangle = radians(5);
    float beamdistrib = 2;
    float samples = 1;
    float blur = 0;
    float bias = -1;
)
{
    uniform vector A = (to - from) / length(to - from);
    float cos_coneangle = cos( coneangle );
    float cos_delta  = cos( coneangle - conedeltaangle );

    illuminate( from, A, coneangle )
    {
        float cosangle = L.A / length(L);

        color atten = pow(cosangle, beamdistrib) / (L.L);
        atten *= smoothstep(cos_coneangle, cos_delta, cosangle);
        atten *=
            transmission( Ps, from,
              "samples", samples,
              "samplecone", 0,
              "bias", bias );

        Cl = atten * intensity * lightcolor;
    }
}
Listing 7.3: Ray traced shadows using transmission()


7.3.3 Ray Labels

Ray labels are identifiers attached to traced rays. Labels are specified using the `label' optional parameters that is supported by all ray tracing shadeops. A shader can then obtain the label of the current ray (the one that caused the shader evaluation) using the rayinfo() shadeop (see rayinfo shadeop).

  ...

  uniform string ray_label = "";
  rayinfo( "label", ray_label );

  if( ray_label == "approx" )
  {
    ... do something ...  
  }
  else
  {
    .. do something more accurate
  }
  ...

7.3.4 Trace Group Membership

All ray tracing capable shadeops accept a "subset" parameter which tells 3Delight that traced rays intersect only with geometry which is part of the given group(s). For example:

color env = trace( P, Nf, "subset", "nearby" );

Intersects only objects that belong to the "nearby" group. The following table lists the meaning of the various strings which can be specified as subset:

""
Objects which don't belong to any group.
"group1"
Objects which belong to group1.
"group1,group2"
Objects which belong to group1 or group2.
"+group1"
Objects which belong to group1 or which don't belong to any group.
"-group1"
Objects which don't belong to group1.
"group1,group2 & -group3"
Objects which belong to group1 or group2 and also do not belong to group3. & can be used any number of times to combine the other forms above.

Additionally, it is possible to specify a default subset in case ray tracing functions do not specify one. This is achieved using Attribute "grouping" "tracesubset".

7.3.5 Ray Tracing Displacements

By default, 3Delight renders ray traced displaced geometry using bump mapping, meaning that only surface normals are affected by displacement shaders, not geometry. It is possible to correctly render such geometry in ray tracing by using:

Attribute "trace" "displacements" [1](48)

It is important to note that there is an additional cost when using true geometric displacements in the context of ray tracing, for the following reasons:

  1. 3Delight needs more memory to hold displaced geometry (even though it uses a caching mechanism to reduce memory allocations).
  2. Displacement bounds expand primitives' bounding boxes, this has a bad effect on ray tracer's space partitioner, meaning less efficient ray / primitive intersections. It is clear that badly set displacement bounds (or extreme displacement) can make ray tracing of true displacements very costly.
  3. For many primitives, geometrical intersections are less efficient than the analytical algorithm 3Delight uses for non-displaced ray tracing.

ray traced displacements enabled ray traced displacements disabled
Figure 7.1: Raytraced displacements become necessary when silhouette details and self shadowing are important.

7.3.6 Oversampling and Derivatives

All ray tracing functions accept a parameter to specify the number of rays to trace. Tracing many rays is the only way to perform spacial and temporal (motion-blur) anti-aliasing when using ray tracing(49). Follows a table that shows, for every ray tracing shadeop, the default number of rays traced and how to modify the default behavior.

trace()
shadow()
environment()
transmisson()
Traces only one ray if `samples' is not specified. The following examples traces 16 rays for better anti-aliasing:
	color ref = trace( P, R, "samples", 16 );
occlusion()
indirectdiffuse()
Traces the number of rays specified using the following attribute (see section Global Illumination Attributes):
Attribute "irradiance" "integer nsamples" [n]
The default is 64 samples. If the optional float samples parameter is specified to the shadeop, it overrides the `nsamples' attributes (see occlusion shadeop).
gather()
Traces the number of rays specified to the function.
Automatic shadows
Traces the number of rays as specified by the following attribute:
Attribute "light" "integer samples" [n]
The default value is 1.

3Delight automatically computes ray differentials on every traced ray, this means that first order derivatives are available in shaders called in a ray tracing context. This ensures that derivative functions such as Du(), Dv() and Deriv() (see section Mathematics) behave properly even after many reflections and refractions.

7.3.7 The Ray Tracing Hider

3DELIGHT uses an efficient REYES algorithm to render objects visible from the eye and uses a ray tracer for effects such as reflections and refractions. By selecting the `raytrace' hider however, 3DELIGHT can use its ray tracer to render primary visibility. The following example sets the ray tracer as the primary renderer and switches off jittering:

Hider "raytrace"
  "int jitter" [0]

The ray tracer is slower than the default REYES renderer but it could prove useful in some cases:

The ray tracing hider produces the same quality images as the REYES hider and supports all the features you expect from a high end renderer: motion blur, depth of field and correct derivatives in shaders.

3Delight 10.0. Copyright 2000-2011 The 3Delight Team. All Rights Reserved.