CSE 457: Introduction to Computer Graphics


Project 3 help session

This session is to help you get started with the fourth project: a raytracer. There is a description of the project is available. Topics this help document covers:


Supplemental Documentation to Project 4: Trace

CSE 457: Introduction to Computer Graphics


Trace Programming Details

Using the Starting Point Program

A scene may be loaded from the command line by typing the name of the Inventor scene file (.iv) after trace, for example, 'trace spheres.iv' will load spheres.iv. The trace program is paused when it begins, so to start tracing the scene, select "Continue" from the "Trace" menu. The spheres in the scene should appear as flat disks, since the routines that compute the color of each pixel have been gutted for your educational benefit.

There are several inventor models located in /cse/courses/cse457/models/. To create or alter scenes, you may use a text editor or a modified version of 'SceneViewer', an Inventor demo program. The modifications allow you to save the lights and camera in a scene file, set an object's reflection and refraction, and run the trace program on the current scene by selecting a menu item. Be warned: a few bugs in SceneViewer have been fixed, but several still remain. The SceneViewer program is located in /cse/courses/cse457/bin/SceneViewer, and the source code is in /cse/courses/cse457/src/SceneViewer/. You may change the options SceneViewer gives to the trace program by adding a line to either your .Xdefaults or .Xresources file, such as the following (note the '-' at the end):

SceneViewer*trace: /homes/isgi/zhuw/457/trace/trace -s 640x480 -m -

The syntax and meaning of the trace program's command line options can be displayed with the -h option:

Trace Inventor ray tracer, version 0.0
Usage:  trace [options] [SceneFile.iv]
options:  -      Read scene from stdin
          -d#    Set maximum levels of ray recursion
          -r#    Set minimum ray contribution (float) for recursion
          -c     Command line only (no window)
          -m     Mesh image
          -q#    Set image quality
          -s#x#  Set image size to width x height
          -ofile Output resulting image to file
One such option, -m, tells the trace program to represent the image as a triangulated mesh rather than a bitmap. The mesh allows for a progressive adaptive refinement of the image and permits the user to better zoom and pan. To pan the image interactively, left-click on the image and drag the mouse, or to zoom, middle-click and drag.

Another option, -s WidthxHeight, allows the size of the output image to be set. You will probably want to speed-up your traces by setting the size of the bitmap to something small, say 50x50, rather than the default of 320x200. Also, a menu option alows you to resize the image as well, Image/Resize Image....

The command line option specifying an output file allows you to render images off-line. That is you can run trace as a background task, log off, and the trace will proceed until the image completes. Please be considerate when rendering off-line images by using the unix commands nice (run the task at a lower priority) and at (specify some later time for the task to start running) (See the man pages for details.)


Hierarchy of files

This program has quite a few source files associated with it, but don't be alarmed, because most of your of your work on this project will be concentrated in only a few files. In fact, all of the required extensions require modifying only the RayTrace() function in RayTrace.c++. Antialiasing and the like will be implemented in the Trace() function of trace.c++, and user-interface changes (for adding menus) are made in ui.c++ and trace.uil. Here are a list of some of the most important of the files:

Time Line

Here is a rough time line to give you a guide on where you should be during this project.


RayTrace() Function

The function in which you will implement all the required extensions is the RayTrace function in RayTrace.c++:
SbColor 
Raytrace(SceneInfo *sceneInfo, RayInfo *rayInfo);
This function calculates the color of a ray that is cast into the scene. Refer to your handout for the pseudo-code of a ray-tracing implementation.

RayInfo

Information about rays that we cast into the scene are kept in the RayInfo structure, which is defined in trace.h:
typedef struct
{
     SbVec3f          startPos;
     SbVec3f          vecDir;
     long             depth;
     float            contrib;
} RayInfo;
The fields vecDir and startPos indicate the ray's direction and origin, respectively. Also in the structure is a depth field which holds the current level in RayTrace() (e.g.,. initial rays have depth 0, spawned rays have depths > 0). Similarly, the contrib field holds how much (multiplicative) contribution a ray has to a pixel's color value.


SceneInfo

The function RayTrace() has two arguments, one of type SceneInfo * and one of type RayInfo *. As the name suggests, SceneInfo contains information about the scene being traced. The SceneInfo structure, defined in Util.h, has five fields of interest to you: root, lights, env, camera, and options.
typedef struct
{
     SoSeparator   *root;
     SoGroup       *lights;
     SoEnvironment *env;
     SoCamera      *camera;
     Options       *options;
     GroupStack    *seps;
     RayCastInfo   rayCastInfo;
} SceneInfo;
The options field is explained below. The camera is used to calculate the initial rays from the eye point; this is done for you by the routine that calls RayTrace(). The env(ironment) field points to a SoEnvironment node, which stores the global ambient color, the attenuation, and the fog color, (in case you want to add them). The lights field is a pointer to a SoGroup node containing a list of lights, which can be a mixture of SoDirectionLight, SoPointLight, and SoSpotLight. To get the ith light or find how many lights there are, see the SoGroup (x)man page. The routines for SoGroup will return a pointer to a generic node, so you will need to find out what type of light it is and cast the pointer returned into a node of that type. To determine which type of light it is, you should call something like isOfType(SoPointLight::getClassTypeId()) in C++ or SoNodeIsOfType(light, SoPtLtGetClassTypeId()) in C. The root field points to the root of the scene graph, and you will want to apply SoRayPickAction to it.


Casting Rays

You will be using the RayIntersectScene procedure in RayCast.c++ to determine which objects in the scene graph are intersected by a ray. In turn, this procedure calls either one of the intersection routines customized for spheres - and possibly other object types (see Approved Bells & Whistles section of the project 4 handout), OR it will revert to calling Inventor's ray intersection class, "SoRayPickAction", if your scene contains any objects that aren't 'supported' by a custom intersect routine. In either case, intersection data is returned in Intersection Structures (IS's) (see Getting Intersection Data on how to retrieve this data). Each IS holds data associated with a single intersected point. That is: the point's coordinates, surface normal, material index, texture coordinates, and a pointer to the intersected object's SuperState node.

Getting Intersection Data

After the RayIntersectScene has been applied to the scene graph, you can obtain the closest intersected point with a call to GetIntersectionPoint , or you can request an arbitrary point from a list of points with a call to GetIthIntersectionPoints. To find out how many intersections were found during RayIntersectScene, use GetNumIntersections point, then be sure not to index a nonexistent point with GetIthIntersectionPoint!. The intersection data is returned in an Intersection Struct (IS):
typedef struct 
{
    SuperState *superState;
    SbVec3f     pickedPoint;
    SbVec3f     normal;
    SbVec4f     texCoords;
    int         materialIndex;
} IS;
These fields names are pretty much self explanatory. Note that you will want to flip the surface normal when computing Phong illumination if the angle between it and the ray direction exceeds 90 degrees.


Inventor Basics

Inventor is a library that allows C++ (and C) programmers to more easily build object oriented, three dimensional graphics programs. The library is comprised of a number of C++ classes, the names of which begin with either Sb (Scene Basic) or So (Scene Object). The Sb objects are simple and don't use reference counts to deal with memory deallocation, whereas the So objects are more complicated.

Inventor has facilities to create, view, and manipulate scenes of three dimensional objects. These scenes are represented by directed graphs of nodes. The nodes fall into several categories: some are basic shapes (cube, sphere, cone, mesh, spline surface); some describe the attributes of a shape (color, transparency, control points to use), some are transformations (rotate, translate, scale); some (called group nodes) contain lists of pointers to other nodes, and there are many others. One node is at the root of a scene graph, from which every other node in the graph is descended. Cycles in the graphs are not permitted.

There are several actions that may be applied to a scene graph, such as rendering the scene with OpenGL, writing the graph to a file, searching for particular nodes, finding a bounding box, and picking the nodes that are intersected by a given ray. These actions typically traverse a scene graph from the top (root) down, going left to right within each group node. In many cases, a state is kept during traversal. For example, when finding nodes that are intersected by a ray, the current transformation matrix must be calculated -- otherwise when Inventor tried to determine whether some object in the graph was intersected, it wouldn't know where the object was positioned in space.

NOTE: You may find additional information in the online book Inventor Mentor. Also, for those who are curious, the source code to some example programs can be found in /usr/share/src/Inventor, and a few executable Inventor programs are in /usr/sbin/iv*.


SuperState

Once you have the point of intersection, you will want to determine the color of the ray by calculating the shading for the intersected object. To do so, you need to find the object's material, which is composed of several components: ambient, diffuse, specular, reflectivity, and transparency. Rather than doing backwards O(N) searches for nodes that hold these attributes, you may access the attributes in O(1) with the fields of the SuperState node, which was created for this very purpose. (The source code to the SuperState node is in SuperState.h and SuperState.c++.) After the scene file is read and before the ray-trace begins, the raw scene graph is transformed into a simpler one. The new scene graph contains a special type of group node called a SoSeparator, which saves the current traversal state before an action enters the SoSeparator and restores the state when it exits. The first child of all but the root SoSeparator is a SuperState node, and its remaining children are shapes (cubes, spheres, quadmeshes in the form of extruded surfaces, etc.) that have the attributes defined in the SuperState. You won't need to deal with many of the attributes of the SuperState, only the fields ambientColor, diffuseColor, specularColor, shininess, transparency, reflection, refraction, and textureImg. Another field, auxText, can be used to pass additional information to the ray-tracer by using SoLabel nodes.

Options

When the program is run, it initializes an Options structure by parsing the command line. The file Options.h defines the Options structure:
typedef struct 
{
     char     sceneFilename[MAXPATHLEN + 1];
     char     bitmapFilename[MAXPATHLEN + 1];
     SbVec2s  bitmapSize;
     unsigned maxRayDepth; // Recurse at most this deep in RayTrace()
     float    minRayContrib; // Recurse in RayTrace() when ray's contrib is no less this
     SbBool   interactive; // Want windows?
     SbBool   outlines;    // Draw mesh triangles with outlines
     SbBool   antialias; 
     unsigned quality;     // Exact meaning undefined
     ImgMode  imgMode;
} Options;

Options.c contains a couple functions to parse and display usage for the command line options. This structure should not be needed for the required part of the assignment.


Send questions/comments to Ben (ben@cs)