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. 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.
First of all, download the support code archive
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 Windows version provides this library and its headers.
Unzip the archive, and specify the command-line arguments in the Debugging options, or run the program from a command line.
Download the file p2.tgz into some folder. Open console and go to that folder. Then type
tar xvzf p4.tgz
cd p4/proj4
make
./proj4 ../scenes/simple.sce s.png
View the resulting file in some image viewer and you should see an empty image with blue background.
Overview of C++ ClassesThe 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()
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 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.
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 (note the change from X3D
spec). 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 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
pointsn in the jittered sampling code of
section 10.11.1. You will need to modify code within RayTracerT::TraceAll()
function to introduce jittered samples. Feel free to write your own auxilliary
functions to achieve this goal.
10 points
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 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 pointsMake sure your code compiles and works on CAEN Linux or Windows machines. You will need to turn in
We'll post the detailed submission instructions closer to the deadline.
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' as long as you respect the deadline. The submission folders will also be closed for writing right after the deadline.
Test the compilation: Your submission must compile without errors and warnings on supported platforms. Mention the platform in your writeup. Code that doesn't compile will be heavily penalized.