In this assignment you will implement a basic raytracer. To allow you to focus on the nuts and bolts of the actual ray tracing, you are provided with a host of data structures for managing the ray traced objects, linear algebra functions (vector and matrix objects and operations), a function for loading scene graph files into a prescribed node tree stucture, a BMP image file importer/exporter (for images and textures, etc), and a couple of supporting data structures for lights, materials, etc.

Additionally, a utility is provided to allow you to view your rayfiles. This will help you out in debugging your code and ensuring that the images come out looking as they should. The utility is implemented in OpenGL and so does not support recursive ray-casting and transparency, but at least for the first few parts of the assignment your image should agree with the image generated by the viewer. The viewer is available in both Linux and Windows formats (you might need glut32.dll if you're using Windows).

An overview of the

codeyou will be using can be found here.An overview of the

`.ray`

filesyntaxcan be found here.

The assignment is worth 20 points. The following is a list of features that you may implement. The number in parentheses corresponds to how many points it is worth. Options in

boldare mandatory.

(1)ModifyRayTrace(const char*fileName,intwidth,intheight,intrLimit,floatcLimit) (in`ray.[cpp/h]`

) to generate and cast rays from the camera's position through pixels to construct an image of the scene.(1)Implement theGroup::intersect(Rayray,IntersectionInfo&iInfo) (in`group.[cpp/h]`

) to cast rays through scene-graph nodes. For now ignore the local transformation and simply compute the intersection properties for the closest intersection within the list ofShapes associated to theGroup.(1)Implement theSphere::intersect(Rayray,IntersectionInfo&iInfo) (in`sphere.[cpp/h]`

) method to compute ray intersections with a sphere.(2)Implement theTriangle::intersect(Rayray,IntersectionInfo&iInfo) (in`triangle.[cpp/h]`

) method to compute ray intersections with a triangle.(1)ModifyGetColor(Scenescene,Rayray,IntersectionInfoiInfo,intrDepth,floatcLimit) (in`ray.[cpp/h]`

) to return the color at the point of intersection using the ambient and emissive properties of theMaterial, and call this function inRayTrace(const char*fileName,intwidth,intheight,intrLimit,floatcLimit) to compute the color at a point of intersection.(1)To obatin the diffuse color contribution of the lights at the point of intersection, implement:

PointLight::getDiffuse(Point3DcameraPosition,IntersectionInfoiInfo) (in`pointLight.[cpp/h]`

);SpotLight::getDiffuse(Point3DcameraPosition,IntersectionInfoiInfo) (in`spotLight.[cpp/h]`

); andDirectionalLight::getDiffuse(Point3DcameraPosition,IntersectionInfoiInfo) (in`directionalLight.[cpp/h]`

).(1)To obtain the specular color contribution of the lights at the point of intersection, implement:

PointLight::getSpecular(Point3DcameraPosition,IntersectionInfoiInfo) (in`pointLight.[cpp/h]`

);SpotLight::getSpecular(Point3DcameraPosition,IntersectionInfoiInfo) (in`spotLight.[cpp/h]`

); andDirectionalLight::getSpecular(Point3DcameraPosition,IntersectionInfoiInfo) (in`directionalLight.[cpp/h]`

).(1)Implement:

PointLight::isInShadow(IntersectionInfoiInfo,Shape*shape) (in`pointLight.[cpp/h]`

)SpotLight::isInShadow(IntersectionInfoiInfo,Shape*shape) (in`spotLight.[cpp/h]`

)DirectionalLight::isInShadow(IntersectionInfoiInfo,Shape*shape) (in`directionalLight.[cpp/h]`

)(1)Use theLight::getDiffuse,Light::getSpecular, andLight::isInShadowmethods you implemented to modifyGetColor(Scenescene,Rayray,IntersectionInfoiInfo,intrDepth,floatcLimit) (in`ray.[cpp/h]`

) so that the returned color takes into account the diffuse and specular contribution of the light sources. (Taking into account whether or not the point of intersection is in shadow with respect to a particular light.)(1)Modify the implementation ofGroup::intersect(Rayray,IntersectionInfo&iInfo) (in`group.[cpp/h]`

) to take into account the local transformation of theGroup. (You can do this by using the transformation to convert the ray into object coordinates, computing the intersection, using the local transformation to convert intersection properties back into world coordinates, etc.)(1)Modify the implementation ofGetColor(Scenescene,Rayray,IntersectionInfoiInfo,intrDepth,floatcLimit) (in`ray.[cpp/h]`

to recursively cast reflected rays at the point of intersection and add the reflected color contribution to returned color value.(1)Modify the implementation ofGetColor(Scenescene,Rayray,IntersectionInfoiInfo,intrDepth,floatcLimit) (in`ray.[cpp/h]`

) to recursively cast refracted rays through the point of intersection and add the refracted color contribution to returned color value. (For now, you should ignore the refraction index.)- (1) Implement a jittered supersampling scheme to reduce aliasing by casting multiple rays per pixel, randomly jittered about pixel centers, and averaging the radiance samples.
- (2) Accelerate ray intersection tests with hierarchical bounding boxes. To do this you will have to:

- Implement
BoundingBox::BoundingBox(Point3D*pList,intlistSize) constructor. (This will create a box containing the specified list of points.)- Implement
BoundingBox::operator+(BoundingBoxb) method. (This will return a bounding box which contains the union of the two bounding boxes.)- Implement the
BoundingBox::intersect(Rayray) method. (This will return the distance along the ray to the nearest point of intersection with the bounding box.)- Implement the
BoundingBox::transform(Matrixm) method. (This will return the bounding box containing the transformed -- no longer axis aligned -- bounding box.)- Implement the
Shape::getBoundingBox(void) for each of theShapesubclasses that you have implemented. This method will have to return the bounding box for that shape. Additionally, when modifyingGroup::getBoundingBox(void) you will have to accumulate the bounding boxes of all the childShapes, transform them, find the bounding box of the transformed bounding box, store that and return it. (Note: When the parser is done reading the .ray file it automatically calls theShape::getBoundingBox(void) method for the root node, so that if you have implemented this method for all of the subclasses ofShape, the bounding boxes are already in place to be used for intersection queries, and you do not have to reset them.)- Implement
Group::intersect(Rayray,IntersectionInfo&iInfo) to support testing ray intersection with the bounding box before testing for intersection with all childShapes.- Optimize the bounding box hierarchy so that when
Group::intersect(Rayray,IntersectionInfo&iInfo) is called, theGroupchecks all the bounding boxes first, chooses the one closest to theRay, tests for intersection with theShapecorresponding to the bounding box and only tests thoseShapes whose bounding box intersection is closer then the current closest intersection point.- (2) Modify
Triangle::intersect(Rayray,IntersectionInfo&iInfo) (in`triangle.[cpp/h]`

) to return the texture coordinates at the point of intersection and modifyGetColor(Scenescene,Rayray,IntersectionInfoiInfo,intrDepth,floatcLimit) to support texture mapping (with bilinear interpolation of texture samples).- (1) Use the index of refraction and Snell's Law to calculate the correct direction of rays trasmitted through transparent surfaces and modify
GetColor(Scenescene,Rayray,IntersectionInfoiInfo,intrDepth,floatcLimit) appropriately.- (1) Treat point/spot lights as having a finite 'area' and cast a collection of rays during shadow checking to generate soft shadows. That is, if all shadows rays are blocked or unblocked we have zero or full lighting from the source in question just as before, but if a fraction of the shadow rays are blocked the light is only partially attentuated. Something randomized and/or adaptive scheme should be used to avoid banding.
- (1) Implement the
Box::intersect(Rayray,IntersectionInfo&iInfo) (in`box.[cpp/h]`

) method to compute ray intersections with a box.- (1) Implement the
Cylinder::intersect(Rayray,IntersectionInfo&iInfo) (in`cylinder.[cpp/h]`

) method to compute ray intersections with a cylinder.- (1) Implement the
Cone::intersect(Rayray,IntersectionInfo&iInfo) (in`cone.[cpp/h]`

) method to compute ray intersections with a cone.- (1) Modify
Sphere::intersect(Rayray,IntersectionInfo&iInfo) (in sphere.[cpp/h]) to return the texture coordinates at the point of intersection (longitude and latitude) and modifyGetColor(Scenescene,Rayray,IntersectionInfoiInfo,intrDepth,floatcLimit) to support texture mapping (with bilinear interpolation of texture samples).- (1) Implement procedural texture mapping with Perlin noise functions to create 3-D solid wood, marble, etc.
- (1) Implement
bump mappingfor either or both texturing schemes.- (1) Implement
depth-of-fieldcamera effects.- (1) Simulate the behavior of a
.real camera lensby implementing the procedure in this SIGGRAPH paper- (2)
Accelerate ray intersectionswith grid, octree or BSP spatial data structures.- (?) Impress us with something we hadn't considered...
By implementing all the required features, you get 13 points. There are many ways to get more points:

- implementing the optional features listed above,
- (1) submitting 3D models you constructed,
- (1) submitting images for the art contest,
- (1) submitting a
`.mpeg`

movie with a sequence of ray traced images resulting from a continuous camera path (e.g., use the`makemovie`

command on the SGIs), and- (2) winning the art contest.
It is possible to get more than 20 points. However, as in the previous assignment, after 20 points, each point is divided by 2, and after 22 points, each point is divided by 4.

You should use the code available here (2.tar.gz, 2.zip), as a starting point for your assignment. We provide you with:After you copy the provided files to your directory, the first thing to do is compile the program. If you are working on a Windows machine, double click on

`ray.[cpp/h]`

: Code responsible for casting rays, calling intersection methods, computing colors, etc.`shape.h`

: Abstract base class that all shapes must implement.

`group.[cpp/h]`

:Shapesubclass describing a scene-graph.`rayFileInstance.[cpp/h]`

:Shapesubclass describing the scene graph specified in a .ray file.`triangle.[cpp/h]`

:Shapesubclass describing a triangle.`sphere.[cpp/h]`

:Shapesubclass describing a sphere.`cone.[cpp/h]`

:Shapesubclass describing a cone.`cylinder.[cpp/h]`

:Shapesubclass describing a cylinder.`box.[cpp/h]`

:Shapesubclass describing a box.`line.[cpp/h]`

:Shapesubclass describing a line segment.`light.h`

: Abstract base class that all lights must implement.

`pointLight.[cpp/h]`

:Lightsubclass describing a point light.`directionalLight.[cpp/h]`

:Lightsubclass describing a directional light.`spotLight.[cpp/h]`

:Lightsubclass describing a spot light.`main.cpp`

: This parses apart the command line arguments and invokes the raytracer.`scene.[cpp/h]`

: Code for the classes that store environmental information, textures, materials, rayFiles, etc.`geometry.[cpp/h]`

: Most of the code for the geometric manipulation you will need (matrix multiplication, vector addition, etc.)`boundingBox.[cpp/h]`

: Code for defining bounding boxes.`bmp.[cpp/h]`

: Code responsible for reading and writing BMP files.`implemented.[cpp/h]`

: Code defining a global flag that specifies if unimplemented methods should announce themselves when they are invoked.`RayFiles/`

: Directory containing a variety of .ray files.`tracer.dsp`

: Visual C++ project file for Windows platforms.`Makefile`

: Makefile suitable for UNIX platforms.`viewer`

: A Linux-compiled ray-file viewer to look at .ray files. (Note that the code for this assumes that if you are looking at the front of the triangle the vertices are indexed in counter-clockwise order.)`viewer.exe`

: A Windows-compiled ray-file viewer to look at`.ray`

files. (Note that the code for this assumes that if you are looking at the front of the triangle the vertices are indexed in counter-clockwise order.)`tracer.dsp`

and selectbuildfrom the build menu. If you are developing on a UNIX machine, type`make`

. In either case an executable called`tracer`

(or`tracer.exe`

) will be created.

The program takes in to mandatory arguments, the input (`.ray`

) file name and the output file name (`.bmp`

). It is invoked from the command line with:Additionally, you can specify image height, image width, recursion depth and contribution limit as follows:`% image -src in.ray -dst out.bmp`

Feel free to add new arguments to deal with the new functionalities you are implementing. Just make sure they are documented.`% image -src in.ray -dst out.bmp -width w -height h -rlim r -clim c`

The following functions have not been completely implemented:

RayTrace(const char*fileName,intwidth,intheight,intrLimit,floatcLimit) (in`ray.[cpp/h]`

);GetColor(Scenescene,Rayray,IntersectionInfoiInfo,intrDepth,floatcLimit) (in`ray.[cpp/h]`

);Sphere::intersect(Rayray,IntersectionInfo&iInfo) (in sphere.[cpp/h])Sphere::GetBoundingBox(void)(in sphere.[cpp/h])Triangle::intersect(Rayray,IntersectionInfo&iInfo) (in triangle.[cpp/h])Triangle::GetBoundingBox(void) (in triangle.[cpp/h])Group::intersect(Rayray,IntersectionInfo&iInfo) (in group.[cpp/h])Group::GetBoundingBox(void) (in group.[cpp/h])Box::intersect(Rayray,IntersectionInfo&iInfo) (in box.[cpp/h])Box::GetBoundingBox(void) (in box.[cpp/h])Cylinder::intersect(Rayray,IntersectionInfo&iInfo) (in cylinder.[cpp/h])Cylinder::GetBoundingBox(void) (in cylinder.[cpp/h])Cone::intersect(Rayray,IntersectionInfo&iInfo) (in cone.[cpp/h])Cone::GetBoundingBox(void) (in cone.[cpp/h])PointLight::getDiffuse(Point3DcameraPosition,IntersectionInfoiInfo) (in pointLight.[cpp/h])PointLight::getSpecular(Point3DcameraPosition,IntersectionInfoiInfo) (in pointLight.[cpp/h])PointLight::isInShadow(IntersectionInfoiInfo,Shape*shape) (in pointLight.[cpp/h])SpotLight::getDiffuse(Point3DcameraPosition,IntersectionInfoiInfo) (in spotLight.[cpp/h])SpotLight::getSpecular(Point3DcameraPosition,IntersectionInfoiInfo) (in spotLight.[cpp/h])SpotLight::isInShadow(IntersectionInfoiInfo,Shape*shape) (in spotLight.[cpp/h])DirectionalLight::getDiffuse(Point3DcameraPosition,IntersectionInfoiInfo) (in directionalLight.[cpp/h])DirectionalLight::getSpecular(Point3DcameraPosition,IntersectionInfoiInfo) (in directionalLight.[cpp/h])DirectionalLight::isInShadow(IntersectionInfoiInfo,Shape*shape) (in directionalLight.[cpp/h])BoundingBox::BoundingBox(Point3D*pList,intpSize) (in boundingBox.[cpp/h])BoundingBox::operator+(BoundingBoxb) (in boundingBox.[cpp/h])BoundingBox::transform(Matrixm) (in boundingBox.[cpp/h])BoundingBox::intersect(Rayray) (in boundingBox.[cpp/h])

You should submit:

- the complete source code with a Makefile,
- any *.ray files you created (optional),
- the .mpeg movie for the movie feature (optional),
- the images for the art contest (optional), and
- a writeup.
The writeup should be a HTML document called

assignment2.htmlwhich may include other documents or pictures. It should be brief, describing what you have implemented, what works and what doesn't, how you created the art contest images and/or movies, and any relavent instructions on how to run your interface.Make sure the source code compiles on the machines in Friend 017. If it doesn't, you will have to attend to a grading session with a TA, and your grade will suffer. Always remember the late policy and the collaboration policy.

Stay tuned for more hints.

- The Ray Tracing News is an invaluable resource for information. The archives contain information of everything you might want to know, and much more...
- Visit the POVRAY site, home of a popular freeware raytracer. Check out the links to the still competitions and animated competitions for inspiration.
- For information about the barycentric coordinate triangle intersection test, check out Ray Tracing News issue RTNv5n3 which focuses on various polygon/ray intersection methods.
- Check your progress by comparing your results with these results.
- Precept notes about class description and lighting equations are available here. (The page describing the getDiffuse method had an error in it and has since been removed.) The PowerPoint version is also available (pdf, ppt).

What is the "contribution limit" and how do I use it?

The contribution limit is used to determine when the value of a color returned by casting secondary rays will be too small to be worth computing. Specifically, if you are casting specular (respectively transparent) rays, then before adding the color obtained by casting secondary rays, you will scale this color by the specular (respectively transparency) coefficient. Since the color coefficients must be between0.0and1.0the specular (respectively transparency) coefficient tells you in advance the upper bound on the brightness of the returned color. Thus if the specular (respectively transparency) contribution is less than the contribution limit you know that you do not need to send off secondary rays in the specular (respectively transparent) direction.What exactly is the scene graph?

The scene graph is basically a Group, which is described here. It is just a linked list of shapes.I implemented some of the optional features, such as supersampling? Should I add new command line parameters for these features?

Yes, by all means. Just remember to document them (in your writeup and in the program itself).It seems that Box, Cylinder, and Cone are all defined to be axis-aligned. What if I want them in some arbitrary orientation?

Just create a Group with the appropriate transformation.What if a ray hits the background?

Just return the background color.How exactly do textures work? Do they replace other colors in the material?

No. The usual way to deal with textures is simply to multiply the texture color by the color the object would have if no texture were present. For example, assume your calculations (disregarding texture) determine that the color of a given pixel should be (0.75, 0.60, 1.00). If the texture color in that point is (0.80, 0.50, 0.75), the final color should be (0.60, 0.30, 0.75).