CS348B Final Rendering Project:

Gummy Bears

by Michael Sego and Kirk Shimano

Original Proposal

Our goal was to create a realistic rendering of Gummy Bears. Gummy products
have a unique, translucent, semi-transparent appearance which is difficult to
describe yet easy to identify. If incorrectly represented, this material could
easily appear to be either too plastic or too liquid. Perfecting this
representation requires careful attention to the reflecting of the light within
the bears and will also require us to buy many bags of Gummy Bears! =-D

To achieve this effect, we would like to implement the techniques described
in Henrik Wann Jansen's paper "Efficient Simulation of Light Transport in Scenes
with Participating Media using Photon Maps." Gummy Bears are a kind of
participating media that will require light transport techniques not provided by
PBRT, and photon mapping is very good at handling participating media in a
manner that increases efficiency and reduces noise. We are also looking at the
techniques described in Henrik's book "Realistic Image Synthesis Using Photon
Mapping" regarding caustics photon mapping.

Specifying the Model

At first we had hoped to model the gummy bear ourselves, so we spent a good
deal of time working on this in Maya. Ultimately, we ended up purchasing a model
online, as the modeling skills of graphics professionals proved to be better
than our own. The model was a triangle mesh with approximately 1,700 vertices.
We put the geometry in a separate pbrt file so it could be easily included

We added a new parameter to pbrt which allowed us to specify whether or not
an object should be treated as a participating media, in which case a volumetric
photon map will be created for it. This parameter included values for the
medium's scattering coefficient and absorption coefficient, as well as a
parameter which affects the granularity of the ray walking algorithm.

One of the first major hurdles we had to overcome was finding a way to
determine when the photon rays intersected the gummy bears. Our first plan was
to add a virtual method to the shape class. This method, called "IsVolumetric",
would simply return false for every shape except the triangle mesh. However, if
we added even a single line of code to the shape.h file, the pbrt default photon
mapping would break down. After nearly two entire nights of working on this
problem, we were unable to make any headway, and instead chose to use a less
elegant, less efficient global array of pointers to all of the gummy bears. At
its worst, our code iterates through this array multiple times per ray cast, but
because there are relatively few bears in each scene, we felt that this extra
cost was acceptable.

PBRT Scene Preparation

Another one of the challenges of this program was understanding how to write
a pbrt scene file and set up appropriate values for the surface integrator, the
sampler, the pixel filter, the lights and the material properties. After a great
deal of experimentation, many renderings of black scenes, and reference
to the Internet and previous assignments for examples, we ultimately created a
scene file similar to the Cornell boxes that we used for most of our testing.
This scene was composed of matte walls to act as our diffuse surfaces, and a
gummy bear to act as our specular surface (and produce the caustic photons.)
After some tests, we determined that using a sphere as an area light source
seemed to produce the best results, and we also determined that the most
gummy-like material provided in pbrt is glass with a 1.3 index of refraction and
equal values for color reflectance and transmission. Once our volumetric photon
map integrator was added, the images looked less like glass and more like gummy
bears.

The Photon Visualizer

As a visual aid to help us better understand exactly where our photons were
being stored, we wrote a program in OpenGL called the Photon Visualizer. As we
were generating photons maps, we would output the coordinates of each stored
photon to a file. Then we would read these coordinates in and display them as
dots in a 3d viewer. The viewer had a number of control mechanisms, allowing us
to rotate, zoom, and translate our viewpoint. We could view the photons in each
of the maps separately (direct, caustic, and volumetric) or all together. Here
is a screenshot of this viewer:

The Volumetric Photon Mapping Algorithm

Populating the volumetric
map

Once our pbrt scene was completed, we were able to move on to the
implementation of the Jensen algorithm. We began by modifying the photon
gathering portion of the default photonmap.cpp. For areas outside the gummy
bears, the photon mapping proceeds as normal. For areas inside the gummy, our
modified code enters into our new ray walking code.

The new ray walking code runs through a basic loop as follows:

Take one step forward along the ray. The size of this step is determined
by a volumetric parameter which is defined in the input pbrt file.

Check and see if this step along the ray takes us to a point outside the
gummy bear. If so, we exit our ray walking algorithm and return to the default
photon shooting algorithm.

Place the coordinates of this new step into the volumetric photon map.

Pick a random number. By combining this random value with the coefficient
of absorption and coefficient of scattering defined in the pbrt file, we
decide whether this ray will scatter or be absorbed.

If the ray is scattered, choose a new random direction for the ray. This
new ray is determined by taking the cross product of the original ray and
one of the primary axes (chosen at random). Originally, we had planned to
refine this algorithm, but because this computationally cheap strategy
provided visually satisfying results, we decided to keep it.

If the ray is absorbed, we break out of this loop and cease evaluation
along this ray.

Return to (1) and continue the process.

The new volumetric map is populated at the same time as the caustic,
indirect, and direct maps. When a sufficient number of photons are collected,
the volumetric photons are stored in a kd-tree to allow for faster lookup in the
future.

Calculating results from the map

After we populated the volumetric photon map, the next step was to modify the
integrator. We used a simplified method to approximate the contribution of the
volumetric photons. When a ray passed into the Li function, we allow the default
pbrt implementation to calculate the base L value. We then check to see if the
ray intersects any of the gummy bears.

If the ray does intersect the gummy bears, we enter into a new algorithm
which modifies the L value. We walk along the ray, using the same step size as
was used to originally shoot the photons. We count the number of photons that
lie within a specified distance from the ray. We then compare the number of
photons to a standardized value (the "volumetric equalizer" specified in the
scene file), and based on this number, decide if the L value should be modified.
Our final algorithm skews these modifications towards the brighter side of the
spectrum, as that provided a more visually satisfying result.

To create our final scene, we generated 24 non-intersecting gummy bears
scattered on a texture mapped matte plane at semi-random intervals. The area
light sphere is directly above the scene slightly to the left of the center. We
used 100,000 caustic photons, 400,000 direct photons, and 2,000 volumetric
photons per object. The scene took approximately 20 minutes to render with a 400
by 400 pixel resolution.