The Story Behind the 2008 Holiday Card

FCs (405K) The image was created by a computer program that uses a computer graphics technique called ray tracing. I do my best to describe the technique here, in the 2006 writeup and here, in the 2005 writeup.

I not only create the card but I also make the tool that makes the card.

This year's card is a bit more ambitious than previous cards. I began working on it in February and only just finished in early October. This year I added a few new geometric and lighting effects that are evident in the card.


Mesh:

I did some meshing around to create some interesting shapes. I built them in a few different ways:

Height Field:

The snow-covered ground surface is a height field mesh that is built from a height map:

A height map is just an abstract-looking image - usually a swirl of shades of gray where the lighter shades are a higher elevation than the darker shades.

ground.height.map (7K)

The ground is constructed from a net (ie, a mesh). This net is overlaid on the height map and the brightness of the height map at a knot gives the knot an altitude. The space between the knots can be treated as flat squares and, if they're small spaces, you don't notice the individual squares but the smooth surface they create.

ground.height.net (4K)
ground (40K)

In addition to the ground surface, the abstract deer is also a height field mesh constructed from a height map.

deer.height.map (4K)

Strip Mesh:

The ribbon on the gift box, the ornament hanging over the tree branch, and the gift tag are strip meshes:

A strip mesh is made from two curves which are joined by many straight lines (like strings of a harp). The area between two adjacent straight lines is small (well, just very thin) and numerous so that the mesh looks smooth and continuous.
In practice, the squares are split into pairs of triangular facets. This is because, even if the 4 sides of a square are straight, the surface can and might be curved. A triangle, on the other hand, is guaranteed to be flat as long as its sides are straight. Flat surfaces are easier to handle.

Other strip meshes in the image are the ribbons around the candy cane and the green ribbon on the ornament. Here the curves are pairs of spirals connected by straight lines resembling a DNA model. The gaps between the curves and the ribs connecting them are filled in with facets for rendering.

tag (23K)


Wires:

A wire is a visualization of a path.
Let me explain... a path is simply the mathmatical formula for a line (curved or straight) in space. When I talked about connected curves for strip meshes above, the curves were really paths. Paths themselves are not visible but points on the paths can determine the corners of facets which are visible.

Successive points along a path can also be used to set up a chain of cylinders that resemble a pipe or wire. If the path is curved, the cylinder ends won't quite match properly so spheres are added at the ends to smooth the joints of the cylinders and to fill in the cracks. If the cylinders are sufficiently short, the visible wire can appear smooth. (Note the curve of the candy cane.)
This kind of assembly is useful enough to be included in the repertoire of my ray tracer as a wire.

I use several wires: the hook on the candy cane follows a circular arc path; the tree uses 3 Hermite paths (trunk, branch and twig); and a couple of spirals and Hermite paths define the curves for runners of the sleigh.

The radius of the visual elements (the cylinders and spheres) along the path of a wire can also grow or shrink as they do in the tree. In this case, the cylinders become cones. Their growth/taper can be constant, exponential or none.

The radius of a circular arc path can grow to make a spiral and the arc can go around more than 360°. Torsion can stretch it like a coil spring. The growth rates of the torsion or radius can be constant or exponential (or no growth).

tree (12K)


Global Illumination:

I implemented a lighting technique called "Global Illumination". This is a more natural way to light a scene.

Traditionally, the scene has been lit by light sources which cast shadows and which produce highlights. The shadows, however, tended to be unnaturally sharp.

Also, in order to give some overall illumination to the scene, ambient lighting has been used. The problem with ambient lighting is that it applies equally to all elements of the scene - even in areas that should be in shadow.

Several years ago, some smart people invented Global Illumination which provides softer, more natural lighting. I tried it out last year in a simple scene but I didn't do it very well and had to touch up the rendering. This year, I tried some variations that simplified the concept and I was able to do a better job. I didn't have to do any post processing this time either.

It's supposed to work like this:
At points where the primary (eye) ray hits a surface, multiple rays are spawned directed in virtually all (ie, sampled randomly) directions from the intersection point. The objects that they hit spawn rays and so on until either the rays exit the scene or until enough generations of spawned rays has been traced. The spawned rays which do not intersect other objects just contribute illumination to the surface. The spawned rays which do hit other surfaces contribute the color of those surfaces factored by their illumination.
Using many of these spawned rays softens shadows and highlights to make them look more natural and causes their colors to blend to a degree. For instance, a white cube will appear pinkish next to a red sphere even though the cube might be dull and not reflect the sphere explicitly. This is called "Radiosity".

I don't calculate "color bleeding". It seems a lot of effort for a very subtle effect that I don't find adds all that much realism. Maybe it's just me. (Apologies to the smart people.)
My approach is much simpler:

From the point on the surface where the initial ray hits, I spawn a number of rays in a generally upwards direction. 40 rays seems to work well for me. Each ray that exits the scene without hitting anything else, contributes a little more illumination to the surface point (eg, 1/40th of the total Global Illumination value). Otherwise, if the spawned ray hits something, then the surface is in shadow from that spawned ray.
Doing this 40 times usually means that, near edges of shadows, some spawned rays will escape the scene and some will hit the shadow-casting object. The effect is a penumbra which looks a little more realistic than the sharp-edged shadow.

I spawn the rays randomly around the shape of a cone whose point is on the object's surface and it opens upward. The wider the cone, the more the spawned rays are spread out.

I use the incident angle (ie, the angle between the light ray and the the surface) in the calculations as I do for diffuse shading of explicit light sources. For example, most of the rays in my upward-pointing cone will just give a glancing blow to vertical surfaces while horizontal surfaces will be bombarded more directly. Diffuse properties of the surface, for example, diffuse reflection and brilliance, affect how the rays will affect the shading.

Ray Tracing with Global Illumination certainly is not a fast process - each pixel in my rendering was illuminated by the 40 spawned rays plus rays directed towards explicit light sources plus rays which were reflected elsewhere into the scene... and the images I printed had 3,150,000 pixels!

hardSleigh (24K)



softSleigh (30K)

So, this year's image was a significant project. A year ago, I had no way to create meshes (ahem, well, I did some ribbons a few years back) or shade the scene as I described above. Over the past year, my time was spent working on the program to "do the math" to generate the new kinds of objects and do the GI shading... then, assembling and shaping the objects to create this year's image.
Now, what am I going to do for next year... ?

Return to Cards of Christmas Past