CMSC 435/634: Introduction to Computer Graphics

Assignment 4
Ray Tracing
Due November 3, 2006

Last update:
Wed Nov 1 15:30 EST 2006

The Assignment

For this assignment, you must write a program that will render convex polygons using ray tracing. The input is in a simple text format called NFF, containing information about the view and objects in the scene. You will read an NFF scene on stdin and ray trace the single image described there. Rays that hit an object should use the color of the object, while rays that do not hit any object should use the background color of the scene. Your output should be an image file in PPM format.


Sample NFF format scenes can be generated using a set of programs called the 'Standard Procedural Databases'. A copy of these programs may be found in ~olano/public/spd3.14/. While NFF format is relatively simple, it does contain many features we will not be using in this assignment. You should be able to read any NFF format file, but ignore anything you do not implement. You may refer to, use, or modify the NFF reading code in spd3.14/readnff.c. For the basic assignment, you should at least handle the "v" viewing specification, "b" background color, "f" object material specification (just the r g b color part), and "p" or "pp" polygon specification (you may assume convex polygons and ignore the per-vertex normals given with "pp").

Of the SPD programs, nurbtest, sombrero, teapot and tetra produce scenes using only convex polygons. mount includes a few spheres and sample includes a cone, though both are still interesting without them. The remaining programs are not that interesting without at least some portion of the extra credit: gears includes concave polygons; balls and shells include many spheres; and jacks, lattice, rings, and tree include many spheres and cones.

Since these programs produce their output on stdout and your ray tracer takes its input on stdin, you can pipe them together:

~olano/public/spd3.14/tetra | ./trace

or since that has 4096 triangles and can be quite slow to raytrace, using the -s option can yield a simpler model with just four triangles that will render much faster:

~olano/public/spd3.14/tetra -s 1 | ./trace


We are using PPM because it is an exceedingly simple format to write. See the man page for 'ppm' for more details.

PPM files can be viewed directly or converted to other image formats for viewing. On the GL systems, you can use "display" to view these files or "convert" convert them into most other image formats.

To create a PPM file, first you should store your image in an array of bytes in y/x/color index order:

unsigned char pixels[HEIGHT][WIDTH][3];

When filling in this array, remember that it is in y/x order, not the more familiar x/y order. The final index is the color component, with r=0, g=1 and b=2. Color values range from 0 to 255. For example, this would store a floating point color value of .5 into the green component at x,y:

pixels[y][x][1]= .5*255;

Once you've filled in the pixels array, actually writing the PPM file is quite simple:

FILE *f = fopen("trace.ppm","wb");
fprintf(f, "P6\n%d %d\n%d\n", WIDTH, HEIGHT, 255);
fwrite(pixels, 1, HEIGHT*WIDTH*3, f);

Extra credit

Implement one or more of: spheres, concave polygons, and/or cones. Including spheres will be worth up to 10 points of extra credit, concave polygons up to 20 points, and cones up to 30 points; resulting in a possible total of 60 points of extra credit for doing all three. Intersections for spheres and convex polygons are covered in the book. Intersections with cones are not, so part of the extra credit will be figuring out the ray/cone intersection.

634 only

Implement any of the non-polygon primitive types available in RenderMan, and extend NFF to specify it. You should handle all of the options RenderMan provides for your primitive. So for the extra credit, closed spheres are sufficient because that's all NFF normally handles, but if you do spheres as your RenderMan primitive, you must also support zmin, zmax and thetamax. You cannot get extra credit for your RenderMan primitive (so if you choose spheres, you cannot also get the sphere extra credit).

Describe your additional primitive as well as your NFF extension in your readme.txt, and check in a sample NFF file that includes your new primitive so we can test it.

Other people's code

Ray tracing is a popular rendering technique, and the internet contains lots of resources for ray tracers in general and things like ray-object intersection in particular. Other than the provided SPD code, and PPM snippet above, YOU MAY NOT USE ANY OUTSIDE CODE. All code that you use must be your own. You are not required to use the provided code, but if you choose not to, you must still write your own.


This is a big assignment. Start NOW, or you will probably not finish.

As before, I recommend laying out a plan of attack before coding. The easiest way to get the basic assignment working is to split convex polygons into a fan of triangles and use the ray/triangle intersection method in the book. Data structures are less of an issue for the basic assignment, but if you plan to attempt any additional primitives, a common effective strategy is to create a generic object class with spheres, polygons, cones, etc. classes derived from it, each with a specialized virtual method to compute the intersection of a ray with that primitive type. To assist in your planning, here is an outline of the steps your ray tracing program will need to do:

  1. Read file format
  2. Calculate image plane and pixel locations in world space
  3. Calculate ray from eye point through each pixel and into the scene
  4. Calculate ray-object intersections, choose smallest/closest
  5. If you have time, add one or more extras

We recommend that you start with a test scene looking from (0,0,0) at (0,0,1) containing a single triangle centered on the Z axis. With a single polygon, it's easier to tell if your loading is working, and with a simple view it is easier to tell if your ray positions are correct. Start trying to find intersections with a 1x1 pixel image, which should give you a ray straight down the Z axis hitting the triangle. Move the triangle around, making sure you get the right answer when you miss the triangle, when your ray is parallel to it, or when it is behind you. You can scale up to 2x2 or 3x3 images to make sure your ray position code is correct. Once you have the basics working, then you can try switching to the SPD scenes. It is also worthwhile getting image output working early. Printing debugging values works OK for one pixel/one triangle scenes, but for larger images and scenes, outputting values other than colors at each pixel can be a valuable debugging tool.

What to turn in

Turn in this assignment electronically by checking your source code into your Assn4 CVS directory by 11:59 PM on the day of the deadline. Assignment 5 builds on this assignment, so take that into account if you are considering late submission. As always, double check that you have submitted everything we need to build and run your submission. Be sure to include a Makefile that will build your project when we run 'make', and a readme.txt file telling us about your assignment. Do not forget to tell us what (if any) help did you receive from books, web sites or people other than the instructor and TA, what (if any) extra credit features you added, and what (if 634) RenderMan primitive you implemented.