Ray Tracing
CS 357 HW4

due Monday, December 1


In this assignment you will write a ray tracer which will display a scene consisting of at least two objects. You should hand in a program and a Makefile, and also a README file that describes your scene. Here are the basic requirements:

A) Position the viewer and the pixel grid anywhere you like. I like to put the viewer on the positive x-axis and make the pixel grid lie in the y-z plane, but you can suit yourself on this. Note that you will need some way to transform between actual pixel addresses and the 3D coordinates of your grid.

B) You need to put at least two objects in your scene and light needs to interact between them in some way. The objects need to be objects for which you can solve the ray intersection problem -- spheres, cylinders, polygons, cubes, etc. Spheres and rectangles aligned with the coordinate axes are the easiest objects to start with. Since you are making only one picture you can can define the objects any way you like. For example, if you can figure out the ray intersection problem for a cube aligned with the coordinate axes, you can use that even if you can't solve the intersection problem for more general cubes.

You will need a light, of course. You will probably find that you can get a nicer picture for not much extra work if you use two lights: perhaps one directly over the objects and one over the viewer. With only one light the shadows look pretty stark unless you use a lot of ambient light, and then they tend to flatten out.

The final picture needs to show some interaction between the objects. One might be a mirror-like surface (with high specular reflection coefficient) that reflects the other, or one might be in the shadow of the another, or one might be transparent and show the other through refraction. One way or another, your picture should show some light interaction. I think this is probably easier to achieve if you have a sphere sitting on a rectangle than if you use two spheres. Note that you can intersect the ray <x, y, z> = <A, B, C> + t<D, E, F> with the rectangle 0< x < 1, 0 < y < 1, z=0 by first finding where the ray intersects the plane z=0 (which is when t = -C/F) and then seeing if the x and y coordinates satisfy 0 < x < 1, 0 < y < 1.

C) You want to represent a ray as a point plus a direction vector times a scalar parameter t. Each object in the scene needs an intersection method (naturally, objects of the same type can share a method). This method should input a ray and return the smallest value of t at which the ray intersects the object. If you want to implement transparency or intersection/difference operations your intersect method should return a vector with all t-values for the ray's intersection with the object. From these t-values you can easily compute the point or points of intersection. You also need some way to compute the object's normal vector at the point of intersection -- this might be computed by each object's intersect method or by some other means, but you can't do lighting calculations without it. Finally, you need a global intersect method that takes a ray, calls each of the objects intersect methods, and returns the intersection point and normal vector for the nearest object hit by the ray -- the object with smallest t-value for it intersection. This method also needs to indicate which object this intersection point belongs to, so you will be able to find the surface properties at this point.

D) The most important function of your ray tracer is its recursive trace method. This will have at least the following arguments:

Your trace method should either return a color (the color of the ray being traced) or else have an additional parameter to return this color. Note that if level = = 0, you may wish to have trace return a background color if the ray does not hit any objects in the scene. For all other levels it is important to have the tracer return black (<0,0,0>) if the ray misses the scene.

The trace method should be easy to write. It calls the global intersect method to find the nearest intersection point. It then does ambient, diffuse, and specular calculations using this point, the surface normal, the surface properties and the properties of the light source (or sources). The diffuse calculation needs another call to the global intersect method to determine if this point is in the light or shade, and the specular calculation needs a recursive call to trace to determine the color of the incoming light that will be reflected specularly. At the end sum all of these components times their weights (the reflection coefficients from the surface properties) to get the color to return.

E) Your program should maintain a bitmap: an NxMx3 array of colors. Each time you trace a ray through another pixel update an entry of this bitmap. In the end you draw the bitmap to the screen with a call to glDrawPixels. Note that as far as OpenGL is concerned your program is running in 2D mode -- you don't need any of the 3D transformations that OpenGL provides. See the example program "simpleImage.c" in Chapter 7 of the PrimerExamples directory. Note also that you do not need to provide any user controls -- it is sufficient for your program to produce a single picture, from a fixed viewpoint. In designing your program you should make liberal use of constants that you can later tweek after you start getting some output.

F) Since there are no controls for moving around the scene, it is sometimes hard for me to see what is going on in student solutions to this assignment. Along with your code and Makefile you should hand in a README file that describes in a few sentences your scene and any details I should pay special attention to.

This is due at midnight on Monday, December 1. Note that this is the Monday after Thanksgiving. If you are planning to go away over Thanksgiving Break you need to schedule your work on this accordingly.