Part 1: Files and Classes

After you have checked out the skeleton and looked at the sample solution (sample_ray.exe or sample_ray), you will probably want to take some time to just look through the code and figure out what is going on where.  In this section, I’ll describe some of the main files and data structures, as well as show what calls what in a typical run of the program.

Tour of Files

To begin, there are quite a few files associated with this project.  The files are grouped into a directory structure that should make it easier to navigate through the source code:

scenes – some sample scenes to raytrace

source – project files, Makefile, sample executables

        src – top-level raytrace code.  Reflection, Refraction, and most other optional effects go here.

                fileio – file input/output code.  You shouldn’t ever need to modify this.

                parser – parsing code; you will want to modify this if you change the .ray format at all.

                scene – classes that represent the “scene.”  Shading, Lighting, and Texture mapping code goes here.

                SceneObjects – geometry classes.  Triangle and Sphere Intersection code goes here.

                ui – the user interface classes.  Add Custom UI Controls here.

                vecmath – vector and matrix math support code.  You don’t need to modify this.


This is a good file to become very familiar with.  It contains two important classes, vec3f and vec4f.  These are a 3-component vector and 4-component vectors, respectively.  Almost all the math you do will be in terms of vectors.  The interface is fairly intuitive, letting you do most algebra operations on vectors using overloaded operators: vector*othervector and vector^othervector for dot and cross products, respectively.

A useful one to know as well.  A ray is basically a position and a direction, someplace in 3D space.  Also defined in this file is an isect, which contains information about the point where a ray intersected an object.  It contains, among other things, a pointer to the object, the surface normal at the intersection, and a “t” value to use in calculating the intersection point.

This is where raytracing begins.  For each pixel in the image, traceRay() gets called.  It gets passed a pointer to the scene geometry information, a ray, and two variables you can use to manage recursion.  (Remember, adding in recursion is your job.)  It calls scene->intersect() to find the first intersection of that ray with an object, and gives you an isect to work with.

When an intersection happens, you need to figure out what color the surface is at that point.  For that, you need a handy shading model, and someplace in the program that knows how to do it.  That’s what goes in this file.  This is the place where color gets calculated from material properties.  Right now it only does one thing: return a diffuse color.

         Another thing that you will find in the material.{cpp,h} files is texture mapping support; we have included a skeleton to make adding this easier.  You need only implement the getMappedValue() function in the TextureMap class to get this going properly (along with calculating uv-coordinates at intersect time).  We have provided you with a lower-level interface to the bitmap which you can use to implement this.

As part of shading, you generally need to look at light sources.  This is the code that knows how to handle them.  This is a good place to deal with attenuation.

parser/Parser.cpp; parser/Token.cpp
Okay, after looking this all over you decide life is too simple, and you’re ready to add extra features, like spotlights for instance.  How do you work them in to the scene graph?  That’s where this file comes in.  As a .ray file is opened, it is converted into a stream of tokens (Token.cpp, Tokenizer.cpp), and then each the tokens are processed in turn into the scene graph (Parser.cpp).  Additions to the file format would start in the processScene() function in this file.

This is where most of the intersection code is written.  Look in here to get an idea of how intersections work and to implement triangle and sphere intersections.

ui/TraceUI.cpp; ui/GraphicalUI.cpp; ui/CommandLineUI.cpp
This is where the user interface code resides.  Look at how the example sliders are implemented.  This is where you will put any custom controls you create.  You can access these controls by adding an “extern TraceUI* traceUI” to the top of each file you need UI access in (the global ‘traceUI’ object resides in ‘main.cpp’).  Then, any code inside that file can make calls to “traceUI->someFunction()”.

         If you want to add new functionality to the UI, you should add a class variable to the base TraceUI class and then add the FLTK control to the GraphicalUI class.  CommandLineUI is if you want to run the raytracer off the command line (often useful for testing), and you may wish to add extra command lilne options for your new features as well.