next up previous contents
Next: Baking Up: Get Your Hands Dirty Previous: Simple Room   Contents


Caustics

Caustics are light patterns that are created when light from a light source illuminates a diffuse surface via one or more specular reflections or transmissions.

Figure 19: Caustics rendered with mental ray
\includegraphics[scale=0.6]{crystalglass.mr.ps}

In figure 19 you see a very simple geometry, a cylinder, being modified by a displacement shader to create an interesting pattern of caustics on the floor. The displacement shader for RenderMan is quite simple:

/* myDisplacement */

displacement
myDisplacement(float factor = 50)
{
  float amp = 0.01 * sin(s*factor*PI);
  P += amp * normalize(N);
  N = calculatenormal(P);
}

Because ray tracing, global illumination, and photon mapping are relative new features of Pixar's RenderMan implementation PRMan there is no general documentation how to add the new features into the the standard interface specified by the The RenderMan Interface. Other renderers like BMRT had GI implemented for a long time and they added their own extensions to the standard interface to deal with the new features. Therefore I can't show how to create caustics with all the RenderMan compatible renderers which do implement their own method. I made a test with PRMan 11.0 which can be found in the documentation of the RenderMan Artist Tools (RAT) but this was a very basic test that caustics can be rendered with PRMan and for the more advanced test I used AIR. However PRMan has a very nice program called ptviewer which can be used to interactively look at a photon map to rotate it and zoom in and out. The basic approach is to render first a pass to create a photon map and store it before you render the final image in the second pass.

So the main difference between the RIB file for the first pass and the RIB file used for the second pass is:

  1. The first pass creates a photon map file by using a special Hider:

    Hider "photon" "emit" 1000000
    

    This line defines how many photons are emitted into the scene. The following line defines that the photons are used for caustics and the filename where to store it:

    Attribute "photon" "causticmap" "prman11caustics.cpm"
    

    The same line is used in the second pass to read the photon map.

    Instead of using a surface shader in the first pass an attribute is defined for the ground plane which receives the photons:

    Attribute "photon" "shadingmodel" "matte"
    

    For the geometry refracting or in this case reflecting the photons you define a similar attribute:

    Attribute "photon" "shadingmodel" "chrome"
    

    Unfortunately this ``shading models'' don't give you the freedom to use real shaders which specify how much photons are reflected or refracted, how many are absorbed or which additional parameters would influence the direction where the photons go.

  2. The second pass is using the file where the photons were stored during the first pass and creates an image. Therefore you have to define a camera and all the other settings which influence the image generation. The first real difference between the two RIB files beside the camera and rendering settings is:

    LightSource "causticlight" 2
    

    This defines an additional light source which is used to add the effect the photons have on the caustics. Beside of defining the filename for the photon map a few additional commands are used to make the objects visible to rays, for shadow creation -- basically ray tracing is activated with that -- and to define a number which is used for collecting the photons nearby.

    Attribute "visibility" "trace" 1 # make objects visible to rays
    Attribute "visibility" "transmission" "opaque" # for shadows
    Attribute "photon" "causticmap" "prman11caustics.cpm"
    Attribute "photon" "estimator" 200
    

    The ground plane is now using a real surface shader called matte which has a diffuse component:

    Surface "matte"
    

    The geometry reflecting the photons gets a simple reflecting surface shader called simplemirror which is taken from the documentation coming with the RenderMan Artist Tools (RAT):

    surface simplemirror ( )
    {
      normal Nn = normalize(N);
      vector In = normalize(I);
      color reflection = Cs;
    
      if (Nn.In < 0) {
        vector reflDir = reflect(In,Nn);
        reflection = trace(P, reflDir);
      }
    
      Ci = Os * reflection;
      Oi = Os;
    }
    

    This shader uses the trace function to shoot a ray into the reflect direction and therefore ray tracing has to be activated.

As I said before I did use AIR for the other tests and created a very similar scene to the one coming with the RAT documentation:

FrameBegin 1
  Format 400 300 1
  PixelSamples 4 4
  ShadingInterpolation "smooth"
  Display "aircaustics.tiff" "file" "rgba"
  Projection "perspective" "fov" 22
  Translate 0 -0.5 8
  Rotate -40  1 0 0 
  Rotate -20  0 1 0

  Option "render" "max_raylevel" [4]
  Option "model" "float unitsize" [0.001]
  Attribute "trace" "bias" [0.05]

  WorldBegin
    LightSource "caustic" 1
    Attribute "light" "string shadows" "on"
    Attribute "light" "integer nphotons" [ 100000 ]
    LightSource "spotlight" 2 "from" [-4 7 -7] "to" [0 0 0]
    "intensity" 100 "coneangle" 0.2

    Attribute "render" "string casts_shadows" ["opaque"]
    Attribute "visibility" "integer shadow" [1]

# Ground plane
    AttributeBegin
      Attribute "caustic" "float maxpixeldist" [ 20 ]
      Attribute "caustic" "integer ngather" [ 75 ]
      Attribute "visibility" "integer camera" [ 1 ]
      Attribute "visibility" "integer reflection" [ 1 ]
      Attribute "visibility" "integer shadow" [ 1 ]
      Attribute "visibility" "indirect" [ 1 ]
      Surface "matte"
      Color [1 1 1]
      Scale 3 3 3
      Polygon "P" [ -1 0 1  1 0 1  1 0 -1  -1 0 -1 ]
    AttributeEnd

# Box
    AttributeBegin
      Attribute "caustic" "color specularcolor" [ 0.4 0.4 0.4 ]
      Attribute "caustic" "color refractioncolor" [ 0.0 0.0 0.0 ]
      Attribute "caustic" "float refractionindex" [ 1.0 ]
      Color [1 1 0]
      Attribute "visibility" "integer camera" [ 1 ]
      Attribute "visibility" "integer reflection" [ 1 ]
      Attribute "visibility" "integer shadow" [ 1 ]
      Attribute "visibility" "indirect" [ 1 ]
      Translate 0.3 0 0
      Rotate -30  0 1 0
      Surface "simplemirror"
      Polygon "P" [ 0 0 0  0 0 1  0 1 1  0 1 0 ]   # left side
      Polygon "P" [ 1 0 0  1 0 1  1 1 1  1 1 0 ]   # right side
      Polygon "P" [ 0 1 0  1 1 0  1 0 0  0 0 0 ]   # front side
      Polygon "P" [ 0 1 1  1 1 1  1 0 1  0 0 1 ]   # back side
      Polygon "P" [ 0 1 0  1 1 0  1 1 1  0 1 1 ]   # top
    AttributeEnd
  WorldEnd
FrameEnd

The main difference between AIR and PRMan is that you can create the caustics in a single pass. Here in short the steps necessary to produce caustics with AIR. This is taken from the documentation coming with AIR:

  1. Add a caustic light source to the scene:

    LightSource "caustic" 1
    

  2. Set the number of photons to trace for each light that is to produce caustics:

    Attribute "light" "integer nphotons" [ 100000 ]
    

  3. For each surface that is to receive caustics, set the minimum number of photons to gather for the irradiance estimate:

    Attribute "caustic" "integer ngather" [ 75 ]
    

    and the maximum distance (in pixels) for gathering photons:

    Attribute "caustic" "float maxpixeldist" [ 20 ]
    

    Larger values produce smoother results and require that fewer photons be used overall.

  4. Primitives that are to receive or produce caustics must be made visible to indirect/photon rays with:

    Attribute "visibility" "indirect" [ 1 ]
    

  5. For each primitive that is to produce caustics by reflecting or refracting light, set the reflective color, refractive color, and refraction index for caustics:

    Attribute "caustic" "color specularcolor"   [ 0.4 0.4 0.4 ]
    Attribute "caustic" "color refractioncolor" [ 0.0 0.0 0.0 ]
    Attribute "caustic" "float refractionindex" [ 1.0 ]
    

    The specularcolor and refractioncolor attributes determine what happens to photons that intersect a surface. Photons will be reflected, refracted or absorbed in proportion to (respectively), the average intensity of specularcolor, the average intensity of refractioncolor, and 1 minus the sum of the average intensities. The sum of specularcolor and refractioncolor should be less than or equal to 1. If the sum is greater than or equal to 1, no photons will be stored on that surface and no caustics will be visible.

In AIR photons can also be saved to a file in the first pass and reused in a second pass. It's in your responsibility to make sure that no photons are emitted in the second pass.

I managed to render a very similar picture to figure 19 with AIR. Nevertheless I have the feeling that mental ray's control over how many photons are considered within a certain radius is more powerful if it comes to creating fine details in the caustics pattern. AIR is measuring a similar distance in pixels which makes it harder to work on details which might be less than a pixel.

Before we talk about how to create caustics with mental ray I will give you the displacement shader for it:

/* myDisplacement.c */

#include <shader.h>

struct myDisplacement
{
  miScalar factor;
};

DLLEXPORT miBoolean 
myDisplacement(
               miScalar* result,
               miState* state,
               struct myDisplacement* paras
               )
{
  miScalar factor;

  /* get parameters */
  factor = *mi_eval_scalar(&paras->factor);
  /* result */
  *result += (0.01 * sin(state->tex_list->x * factor * M_PI));

  return(miTRUE);
}

It shouldn't be a problem to modify the Makefile I have shown earlier to compile this shader. To create the corresponding MI file where the shader and it's parameters are declared should be easy as well. Let's focus on the relevant parts of the MI file which was used to render figure 19:

  1. Caustics must be enabled in the options block.

    options "#opt"
    ...
            caustic          on
            caustic accuracy 700 .05
            caustic filter cone 1.1
            photonmap file "mrphotonmap.cpm"
            photon trace depth       4 4
    ...
    end options
    

    Further options give finer control over the process and help to fine-tune the result. By writing the photon map to a file it is possible to create once a photon map with a lot of photons and change later only parameters which do not effect the process of storing the photons. This process takes a while and will be done before the actual ray tracing of the scene is done. Because of it's nature of being a post-process it's worth to make first a decision about the number of photons to emit and the energy used in the lights by simple tests without fine-tuning. Once that decision is made you can reuse the photon map and play with the parameters like caustic accuracy.

  2. The light source must have an energy statement:

    light "#/obj/light1_obj"
            "physical_light" (
                    "color" 1000.0 1000.0 1000.0, 
                    "cone" 0.9867880932509171
            )
            origin          0 0 0
            direction       0 0 -1
            spread          0.96891242171064473
            energy          1000.0 1000.0 1000.0
            caustic photons 5000000
    end light
    

    It is advisable to use a light shader like physical_light that ensures physical correctness. The energy is distance-dependent and often has to be chosen quite large.

  3. Both the caustic-casting and caustic-receiving objects must have a material that contains a photon shader statement:

    material "#mtl0"
            "dgs_material" (
                "diffuse"   0.8 0.8 0.8,
                "lights"    [ "/obj/light1" ]
            )
            photon "dgs_material_photon" ()
    end material
    ...
    material "#mtl1"
            "dielectric_material" (
                "ior"        0.8,
                "col"        1.0 0.0 0.0,
                "phong_coef" 0
            )
            displace "myDisplacement" ("factor" 60)
            photon "dielectric_material_photon" ()
    end material
    

    Photon shaders may store and either absorb, reflect, or transmit photons.

  4. The material should not have a shadow shader to avoid having light pass through the object twice, once directly and once indirectly.

  5. The caustic-casting material should not be diffuse because diffuse objects spread the light rather than focusing it.

  6. The caustics-receiving material must be diffuse, because otherwise no photons are stored there. Its material shader must support collecting stored photons.

  7. If the caustics-casting object is refractive the index of refraction should be greater than 1.0 to create a focusing effect.

To generate caustics more efficiently, objects can be flagged such that the photons are only emitted toward certain objects and stored only on selected objects. Objects are then divided into caustic-casting (caustic 1 flag) and caustic-receiving (caustic 2 flag), or both (caustic 3 flag), or neither (caustic 0 flag).


next up previous contents
Next: Baking Up: Get Your Hands Dirty Previous: Simple Room   Contents
Jan Walter 2004-02-09