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 TGA image file. The assignment support code includes a routine for reading in the scene files, and the TGA image output library. The support code provides point light sources, and a few geometric primitives such as a triangle, a ball, and a triangular mesh. The specified primitives are affected by the current modeling transformation, and current material properties. The scene description format is described here.
It is highly recommended that you read chapter 10 of our text book: Fundamentals of Computer Graphics, 2nd Edition, by Peter Shirley et al. Most of the implementation details are actually provided in the book.
Support Code% scp -r red.engin.umich.edu:/afs/engin.umich.edu/class/perm/eecs487/proj5 .(Note the 'dot' at the end of line.) The above command overwrites all proj5 files, including any files that you may have already edited. To copy 'old' versions of files while leaving the new untouched (sync-ing operation):
% rsync -avz red.engin.umich.edu:/afs/engin.umich.edu/class/perm/eecs487/proj5 .(Do this from the parent directory containing the proj5 subdirectory.)
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. It is hooked up with the LTGA
image buffer,
that performs the actual image output into a Targa (TGA) file.
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()
should return the pointer to the jittered samples array for the area
lights, and return 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 XVecf
= XVecT<float>
represents a three-component vector,
whereas the XVecdimT
template provides a vector of
arbitrary dimensions. The matrix transform class
XFormf
stores a 3x4 matrix.
This matrix can be applied to points and vectors. When applied to
points represented as XVecf
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": XVecf, XVec2f,
XFormf
.
Note: Implementation details
(and notation) are based on our text book: Fundamentals
of Computer Graphics, 2nd Edition, by Peter Shirley et al.
RayTracerT::TraceAll()
function in
raytracer.cpp. Once that is
done, test the results by running the program. E.g.: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 project 2, 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
pointsu,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 struct 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 pointsRayTracerT::Shade()
and RayTracerT::Trace()
. 15 pointsCylinderT which you should define in
cylinder.h.
Note that 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. As described in
Section 10.8 of the book ("instancing"), a canonical cylinder can be
arbitrarily positioned, oriented, and resized via a transform. (The
same concept should be familiar from projects 3 and 4). 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
pointsn
in the jittered sampling code of
section 10.11.1.
Modify the function MakeJitterSamples()
in
raytracer.cpp 10 pointsAreaLightT
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 pointsMeshT::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