back to table of contents

Reflections
download mac os x application and source code (68k)
Written wednesday december 5, 2001. Developed with apple's free developer tools on mac os 10.1
This document, images, source and application are the intellectual property of forrest briggs. You may freely use the source code i provide. Email me with questions or suggestions.

Update, thursday december 6: i had been incorrectly compositing the diffuse, shadow and reflected components of color. This error is now fixed in the code and the correct method is described in this article.

Reflections

Unlike some other 3d rendering methods, reflections are very simple to generate using ray tracing. In real life, the interaction of light with a reflective object occurs in the following sequence: A light source emits photons. Photons collide with and bounce off of objects (some energy is absorbed by such collisions, which is why we see color.) Photons travel into the Eye. Remember that the method we are employing here is backward with respect to how light actually travels in the physical universe. Thus, in backward ray tracing, a ray is fired from the eye and Bounces off of objects while at each reflection a probabilistic lighting calculation such as the one described in the diffuse shading section is performed. The term "recursive ray Tracing" is often used to describe this process because of the elegant way in which the ray tracing algorithm handles reflections of reflections of reflections of reflections of.... Lets examine this algorithm.

You already know how to fire a ray from any point in space and determine which object it first intersects as well as the color of the object at the point of intersection. In determining That color, we have only analyzed what is called the diffuse component, which is based on the actual color of the object and how much light reaches it. In implementing recursive ray tracing, We now incorporate the reflected component into the color calculation. This is done by first calculating the reflected ray at the point of intersection and then tracing this ray just like any Other. The color that this calculation returns is the reflected component.

The equation for the direction of a reflected ray is R = I - 2*(N·I)*N, where R is the direction vector of the reflection, I is the incident (direction of the incoming ray,) and N is the Normal to the surface. The reflected ray now has a direction, but it must also have an origin. This is the point of intersection between the object and the incident. To analyze this calculation From a different perspective, suppose that you have fired a ray from the eye and it intersects a reflective object. You have probably already calculated the location of the intersection and the Normal at that point for other reasons. To calculate the reflected component, calculate the direction of the reflected ray with R = I - 2*(N·I)*N, where I is the direction of the ray from The eye and set the origin of the reflected ray to the intersection of the ray from the eye and the object. Now trace this ray like any other and use the returned color in calculating the color Of the reflective object at that point.

Unless all of you objects are completely reflective, you will want to attach a coefficient of reflectivity to each object. If this is a number between 0 and 1, where 1 signifies a completely Reflective surface, then c = r*rc + s*d*(1-rc), where c is the final color of an object at a point, r is the reflected component at that point, rc is the Coefficient of reflectivity, s is the shadow coeficient (either 0 indicating that the point is in shadow or 1 indicating that it is illuminated,) and d is the diffuse component. As you explore more advanced shading techniques, you will find that there are many different components that make up the color of an Object at a point. In my model, many of them have a coefficient, all of which add up to 1, which is why d is multiplied by (1-rc). Note that d is the diffuse component after Lighting calculations have been performed, including shadow calculations. Look at a mirror; its brightness at a point is not proportional to the angle between its normal and the light source. Mirrors are close to a surface With a coefficient of reflectivity of 1, which means that at any given point, the brightness is determined solely by what is reflected. A mistake i made in some of my earlier ray tracers in dealing With reflective surfaces was to apply diffuse shading calculations to a surface after mixing the reflected component and a base color. This produced images that looked convincing at times, but it Is an incorrect approach.

Since the previous example, i have restructured the code so that a single function, TraceRay() handles both rays that originate from the eye (also known as primary rays,) as well as reflected rays (secondary rays.) It is important to notice that the first thing this function does is check to see if a certain maximum depth is exceeded. In certain situations, a reflected ray will bounce back And forth between two surfaces forever, which will cause the program to crash. To prevent this, each ray has an associated depth. Primary rays have a depth of 0. A reflection ray from a primary Ray has a depth of 1. At reflection of a reflection has a depth of 2. In general, whenever spawning a new ray, set its depth to the current depth plus one. Renderers that are primarily designed To produce very realistic results often rely on methods of determining maximum depth that take into consideration the contribution to the final color that a given level of reflection makes. The Sequence of reflections is terminated when the contribution is less than a certain part of the whole. For our purposes, an arbitrary maximum depth will suffice.