
Don't see the menu?
|

Programming Assignment 3: Ray Tracer
EECS487 F09 PA3: Ray Tracer
The project is due on Monday 16 November 2009 12:01pm (one minute after noon)
Overview
In this assignment you will implement a simple ray tracer.
Your ray tracer will be a command line application that takes two
parameters: the name
of a scene description file and the name of the output png image file.
The
assignment support code includes a routine for reading in the scene
files and provides point light sources and a few geometric
primitives
such as a triangle, ball, and triangular mesh. The specified
primitives are
affected by the current modeling transformation and current material
properties. The scene description format is described
here.
You should read Chapter 15 of the TP3 textbook and
(accessible only on umich.edu hosts)
excerpts
from Chapter 10 of Shirley, Fundamentals of Computer Graphics, 2nd ed. [FCG2] and the lecture notes on
ray tracing and
distributed ray tracing. You may use or adapt the implementation details provided in the excerpts from FCG2
in this assignment, but be sure to cite it when you do so.
Support Code
First download the support code archive from /afs/umich.edu/class/eecs487/f09/PAs/pa3.tgz that contains the support code, some sample scenes, and a Makefile.
After building the project, the executable should be called with two command-line arguments:
the name of the input scene file (*.sce) and the name of the output png
image file (*.png).
The code needs to be linked against png library, and the compilation
requires the corresponding header files. The CAEN Linux setup provides both
the library and the header files, and no setup is needed (just run make
within the pa3 folder). On Windows, we provide the library and headers in the
common folder. On Mac OS X, you should have installed libpng
as part of PA1. If not, please refer to the instructions in PA1 spec on how
to do so.
This project does not involve OpenGL and its ilk.
Overview of
C++ Classes
The support code defines the following classes.
Class RayTracerT
contains most of the ray tracing functionality; the main loop
that assigns color values to the pixels of the output image, as well as
the ray tracing and shading code. The processing uses the scene object
that was previously initialized, as well as some additional options and
camera parameters.
Class SceneT provides the
interface between the scene and the ray tracer; this includes the
intersection testing between the scene and an arbitrary ray (SceneT::Intersect()
function),
the container of lights, and the background color. The SceneT
class also
stores pointers to all the geometric primitives (gels) present in the
scene.
Class CameraT stores the
perspective camera parameters and provides the ability to initialize
them from the input file information.
Class OptionsT contains
some additional parameters useful for rendering.
The output image is stored in an XImageT
object that provides some basic image manipulation functionality. We
use png image library for image output.
As mentioned above the scene contains the containers of lights and
geometric elements. All lights
should be derived from the ILight
class and provide color and sampling information. The ILight::SamplePosition()
function should produce a sample on the light, possibly using the
index hint provided via ILight::SamplePosition() call.
The ILight::Jitters() function
should return the pointer to the jittered samples array for the area
lights, and return the NULL pointer if the light is a point light. The PointLightT
class is present in the support code but you will have to implement
the area light class.
There are several geometric primitives provided in the code which
are all derived from the IGel
class. Its only function is IGel::Intersect() whose
purpose is
to compute the intersection between a given ray and the primitive. If
there is no intersection the function returns false.
Otherwise the function should return true
and compute the nearest non-negative intersection between the ray and
the primitive and fill in the hitinfo_t structure
with the information about the intersection point material and geometry
(position and normal).
Note that the intersection can be affected by the transformation stored
in the geometric primitive; see below for more details.
In order to simplify your geometric computations we provide the
vector and matrix transformation
classes. The class XVec3f
= XVec<float> represents a three-component vector. The
matrix transform class
XFormf stores a 3x4 matrix.
This matrix can be applied to points and vectors. When applied to
points represented as XVec3f objects, the left three
columns form a 3x3 transformation matrix
and the last column gives an additional translation vector; the result
is a transformed 3D point (see XFormT::apply() function).
The same matrix when applied to vectors, omits the transation part (see
XFormT::applyv() function). The function XFormT::Inverse()
returns an inverse transformation. The function XFormT::applytv()
applies a transpose of the stored
matrix to a vector (this can be useful when dealing with normal
vectors). Note that all of the vector and
matrix classes are templated on the floating-point type of the stored
elements. Since the code uses float data, we typedef the
appropriate type names ending with suffix 'f': XVec3f, XVec2f,
XFormf.
Tasks
- In order to get your first image you will need to add viewing ray
generation code (as described in Section 10.2 of FCG2 and Section 15.4.1 of TP3).
For this, modify the
RayTracerT::TraceAll() function in
raytracer.cpp. Once that is
done, test the results by running the program, e.g.,
% pa3 simple.sce out.png
When you view the image (out.png) you should see a simple scene
rendered with ambient lighting. See camera.h for details of the
camera class (needed to compute the rays). When using scene files that
import meshes (.ob extension) ensure that the .ob file is in the same
directory as your pa3 executable. It is best if all your .sce files
and .ob files are in the same directory as the executable. You could either
copy all your scene files into your code directory or create a link to
the pa3 executable in your scenes directory. The latter is usually a cleaner
solution. 15 points
- Your next step will be to implement the simple shading model
given in equation 12.26 (Section 12.4) of TP3. For this, modify
RayTracerT::Shade()
in raytracer.cpp. Use the material constants and the light parameters
stored in the provided data structures (see material.h and light.h).
Only add contributions from a
given light if there is no occluder blocking that light from the
surface point being shaded (use a ray test for this). The shading
calculations are similar to what you did in PA2, except we don't
deal with spot lights or attenuation due to distance, and we use the
halfway vector instead of the reflection vector when computing the
specular contribution. 10
points
- Add the Phong shading functionality to the mesh class, by
using the
u,v barycentric coordinates computed by the
ray-triangle
intersection routine to compute an interpolated normal at the hit
point. (See mesh.h: the triangle_t structure stores indices of the 3
vertices, and each vertex stores a normal. Vertex indices are given
with respect to the list of vertices stored in the mesh.) Modify the
code in MeshT::Intersect(). 5 points
- Next add specular reflections as described in Section 15.2.1 of TP3.
You will be making recursive calls to the ray-coloring
function, and hence you should limit the maximum recursion level to the
number given in the
scene file. Modify the code inside
RayTracerT::Shade()
and RayTracerT::Trace(). 15 points
- Add a new geometric primitive to the ray tracer -- the cylinder,
represented by the class
CylinderT which you should define in
cylinder.h.
The scene description command for the cylinder does not
include any geometric information. Therefore, your primitive should
define a canonical cylinder whose axis lies along the world z-axis, with radius 1, top plane at
z = 1, and bottom plane
at z = 0 (this is not the same
as the X3D spec). As described in Section 10.8 of FCG2 and Section 15.5.2 of TP3,
a canonical cylinder can be arbitrarily positioned, oriented, and resized via a
transform. (The same concept should be familiar from PAs 2 and 3).
Store this transform as member data in the
CylinderT class. (The
provided
class BallT, defined in ball.h, is similar and can
serve as an
example.) When loading the scene, store the current transformation in
the
cylinder. Modify LoadScene() in loadscene.cpp to create a cylinder
instance with
the current transform applied. 15
points
- You may notice that without any post-processing, the images
created by your simple ray tracer exhibit aliasing. To address this,
add anti-aliasing functionality. It should be
turned on by a non-zero value of the aasample parameter in the input
file. The number specified in the aasample option corresponds
to the number of jittered samples (the constant n in the jitter
sampling code in Section 10.11.1 of FCG2). You will need to modify the code within
RayTracerT::TraceAll() function to introduce jittered samples. Feel
free to write your own auxilliary functions to achieve this goal.
10 points
- Your next task is to add area lights to the scene to create soft
shadows. Section 10.11.2 of FCG2 describes a method for handling area
lights. Implement the simple method based on
random (not jittered) sampling. Derive a new
AreaLightT
class from the ILight interface
and implement the virtual AreaLightT::SamplePos() method
to return a randomly chosen position on the light. Modify LoadScene()
in loadscene.cpp to create an instance of your AreaLightT
class when an area light is specified in the scene file. 5 points
- When you load a large mesh file in your scene, you may notice
that your ray tracer
runtime performance drops significantly. In order to make it faster,
implement a simple bounding sphere test that checks whether the
given ray intersects the bounding sphere of the mesh. Only if this test
is passed should it
proceed to check all the individual triangles of the mesh. Modify your
code in
MeshT::ComputeBV() and MeshT::Intersect().
We recommend you use the provided BallT
class to represent the
bounding sphere. Initialize the bounding sphere in the function MeshT::ComputeBV().
10 points
- Create a scene file, render an image, and include it with your submission. Your scene can contain mesh files
(in obj
format) that you get on the Web or elsewhere (remember to cite your sources), but you should
arrange your
objects and define the viewpoint, scene lights, and material properties
to
produce an attractive image. Be creative!
You can post images you produce by the following:
linux% cd <to where your image.png file is located>
linux% /afs/umich.edu/class/eecs487/scripts/postpng image.png [image2.png ...]
You can then view the images posted at
http://www.umich.edu/~eecs487/.
10 points
- Hand in a short write-up in text format describing: (1)
anything about your
implementation that is noteworthy, including high-level descriptions of
the strategies you used to implement the required functionality, and
(2) feedback on
this assignment, including suggestions for how it should be changed
next time. As well as listing which files were changed and how. 5 points
Submission Guidelines
As with PA1, to incorporate publicly available code in your
solution is considered cheating in this course.
To pass off the implementation of an algorithm
as that of another is also considered cheating.
For example, if the assignment asks you to implement sort using
heap sort and you turn in a working program that uses insertion
sort in place of the heap sort, it will be considered cheating.
If you can not implement a required algorithm, you must
inform the teaching staff when turning in your assignment, e.g.,
by documenting it in your writeup.
The creative portion of this project, Task #9, will take a long time if
you are interested in getting a good score. This involves a lot of
trial and error. The more complex your scene gets, the more time it
will take to render. Do budget for long runtimes in your plan of
action.
Code that doesn't compile will be heavily penalized. Make sure your code compiles and works
on CAEN machines. You will need to turn in:
- the source code files that you have modified, together with
appropriate Makefile and/or IDE project files,
- copies of images generated from Task #9,
- a write-up in text format named writeup-<uniqname>.txt that describes
- Your platform
-
Windows, Linux, or Mac OS X.
- Anything about your implementation that is noteworthy (e.g. a
new command in the sceneformat that you added)
- Feedback on the assignment.
Copy your submission to the following directory on IFS: /afs/umich.edu/class/eecs487/f09/SUBMIT/<uniqname>/pa3.
This path is accessible from any machine you've logged into using your
ITCS (umich.edu) password. Report problems to ITCS.
The timestamp on your source files and your writeup will
indicate the time of submission and if this is past the deadline
your submission will be considered late. Therefore, you are
allowed multiple "submissions" without late-policy implications
as long as you respect the deadline.
Test the
compilation! Your submission must compile without errors
and warnings on CAEN machines. Mention the
platform in your writeup.
General Information
The General
Information section from PA1 applies. Please review it if you haven't
read it or would like to refresh your memory.
|