- For those using Visual Studio:
- If you are using previous version to 2015, for each of the projects (Assignment and JPEG) go to Configuration Properties -> General -> Platform Toolset and change v140 to v120 (or the one supported by your version).
- copy glut64.dll and glut32.lib to x64/Release/

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, BMP and JPEG image file importers/exporters (for images and textures, etc), and a couple of supporting data structures for lights, materials, etc.

An overview of the

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

`.ray`

filesyntaxcan be found here.A description of what your ray-tracer should do as you implement the different parts of the assignment can be found here.

The code skeleton can be downloaded here.

For people compiling under windows who do not have the glut libraries installed on their machine, you will also need to download the library and include the files in same directory as executable.

You should use the code available here (Assignment2.zip), as a starting point for your assignment. We provide you with a number of files, some of which will only need to be modified in future assignments. As in assignment 1, code modification should be relegated to the`*.todo.cpp`

files.

`main.cpp`

: Parses the command line arguments and calls the ray-tracer.`Util/cmdLineParser.[cpp/h]`

: Code for processing the command line arguments.`Util/geometry.[cpp/h]`

: Data structures and methods for a number of geometric objects, including points, rays, planes, and bounding boxes.`Util/geometry.todo.cpp`

: Code stub for bounding box intersection that you will need to implement.`Util/time.[cpp/h]`

: Code for computing the current time, in milliseconds.`Image/bmp.[cpp/h]`

: Code for reading and writing BMP images.`Image/jpeg.[cpp/h]`

: Code for reading and writing JPEG images.`Image/image.[cpp/h]`

: Code for image processing.`Image/lineSegments.[cpp/h]`

: Code for processing line segments.`Image/image.todo.cpp`

: Code stubs for image processing in assignment 1.`Image/lineSegments.todo.cpp`

: Code stubs for line segment processing in assignment 1.`Ray/rayShape.h`

: Abstract base class that all shapes must implement.

`Ray/rayGroup.[cpp/h]`

:RayShapesubclasses describing a node in a scene-graph.`Ray/RayFileInstance.[cpp/h]`

:RayShapesubclass that stores an instance of a scene graph specified in a .ray file.`Ray/rayTriangle.[cpp/h]`

:RayShapesubclass describing a triangle.`Ray/raySphere.[cpp/h]`

:RayShapesubclass describing a sphere.`Ray/rayCone.[cpp/h]`

:RayShapesubclass describing a cone.`Ray/rayCylinder.[cpp/h]`

:RayShapesubclass describing a cylinder.`Ray/rayBox.[cpp/h]`

:RayShapesubclass describing a box.`Ray/rayLight.h`

: Abstract base class that all lights must implement.

`Ray/rayPointLight.[cpp/h]`

:RayLightsubclass describing a point light.`Ray/rayDirectionalLight.[cpp/h]`

:RayLightsubclass describing a directional light.`Ray/raySpotLight.[cpp/h]`

:RayLightsubclass describing a spot light.`Ray/rayCamera.[cpp/h]`

: Class representing a 3D camera.`Ray/rayScene.[cpp/h]`

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

: Code responsible for doing the back-end of JPEG file I/O.`main.cpp`

: This parses apart the command line arguments and invokes the raytracer.`RayFiles/`

: Directory containing a variety of .ray files.`Assignment2.sln`

: Visual C++ solution file for Windows platforms.`Assignment2.vcproj`

: Visual C++ project file for assignment 2 (Windows platforms).`JPEG.vcproj`

: Visual C++ project file for the JPEG library (Windows platforms).`Makefile`

: Makefile suitable for UNIX platforms.After you copy the provided files to your directory, the first thing to do is compile the program. To do this, you will first have to compile the

`JPEG`

library and then compile the`assignment2`

executable.On a Windows Machine

Begin by double-clicking on`Assignment2.sln`

to open the workspace in Microsoft Visual Studios.

- Build the
`Assignment2`

executable:

`Build -> Build Assignment2.exe`

or`F7`

. (If it has not been compiled yet, this will also compile the`JPEG`

library.)On a Unix Machine

- Type
`make`

to compile the`Assignment2`

executable. (If it has not been compiled yet, this will also compile the`JPEG`

library.)

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

) .ray file name, the output image file name (`.bmp`

/`.jpeg`

/`.jpg`

), the dimensions of the output image, the recursion depth, and the cut-off value for early termination. It is invoked from the command line with:Feel free to add new arguments to deal with the new functionalities you are implementing. Just make sure they are documented.`% Assignment2 --in in.ray --out out.bmp --width w --height h --rLimit r --cLimit c`

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

The assignment will be graded out of

- (1)
RayScene::GetRay(`Ray/rayScene.todo.cpp`

):

Generate rays from the camera's position through (i,j)-th pixel of awidthxheightview plane.- (2)
RayGroup::intersect(`Ray/rayGroup.todo.cpp`

):

Cast rays through scene-graph nodes. For now, ignore the local transformation and bounding volume information, and simply compute the intersection properties for the closest intersection within the list ofRayShapes associated to theRayGroup. (The number of shapes associated to the group is stored in RayShape::sNum and the list of shapes is stored in RayShape::shapes.)This method should return return-1.0if there is no intersection. Otherwise, it should return the (positive) distance to the intersection. If the value`mx`

is bigger than zero, then only look for intersections whose distance is closer than`mx`

from the beginning of the ray.- (2)
RaySphere::intersect(`Ray/raySphere.todo.cpp`

):

Compute ray intersections with a sphere. (Ignore the texture coordinates for now.)- (2)
RayTriangle::intersect(`Ray/rayTriangle.todo.cpp`

):

Compute ray intersections with a triangle. (Ignore the texture coordinates for now.)

In computing the intersection of a ray with a triangle, there is a large amount of information that is re-computed, independent of the ray. Rather than computing this information each time, you can pre-compute the re-used information. To this end, the RayTriangle class has members`Point3D v1`

,`Point3D v2`

, and`Plane3D plane`

. You can set these values at the time that the triangle is set by implementing the`RayTriangle::initialize`

method.- (1)
RayScene::GetColor(`Ray/rayScene.todo.cpp`

):

Return the color at the point of intersection using the ambient and emissive properties of theRayMaterialof the intersected shape.

To implement this, you will need to intersect the ray with the root of the scene graph, this is the`StaticRayGroup`

pointed to byRayScene::group.- (2) To obtain the diffuse color contribution of the lights at the point of intersection, implement:

RayPointLight::getDiffuse(`Ray/rayPointLight.todo.cpp`

);RaySpotLight::getDiffuse(`Ray/raySpotLight.todo.cpp`

); andRayDirectionalLight::getDiffuse(`Ray/rayDirectionalLight.todo.cpp`

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

RayPointLight::getSpecular(`Ray/rayPointLight.todo.cpp`

);RaySpotLight::getSpecular(`Ray/raySpotLight.todo.cpp`

); andRayDirectionalLight::getSpecular(`Ray/rayDirectionalLight.todo.cpp`

).- (2) To determine if the point of intersection is in shadow from a particular light source, implement:
In implementing this method, you will cast a ray from the intersection point in the direction of the light source. Keep in mind that for point- and spot-lights, you only need to test for intersections along a line segment (not the full ray), so you can use the

RayPointLight::isInShadow(`Ray/rayPointLight.todo.cpp`

);RaySpotLight::isInShadow(`Ray/raySpotLight.todo.cpp`

); andRayDirectionalLight::isInShadow(`Ray/rayDirectionalLight.todo.cpp`

)`mx`

parameter of theRayShape::intersectto limit the extent of the ray along which intersections are tested.- (1)
RayScene::GetColor(`Ray/rayScene.todo.cpp`

):

Modify the computation of the color at the point of intersection to take into account the diffuse and specular contributions of all of the light sources (RayLight::getDiffuse,RayLight::getSpecular), and using theRayLight::isInShadowmethod to determine if the light contributes.

In order to implement this method, you will need to consider the contributions of all the light sources in the scene. The number of lights is stored in:RayScene::lightNumand the array of lights is stored inRayScene::lights.- (2)
RayGroup::intersect(`Ray/rayGroup.todo.cpp`

):

Modify this method so that it takes into account the local transformation returned by the call:RayGroup::getMatrix. You can do this either by applying the transformation to the shape (which is difficult) or by using the transformation to convert the ray into object coordinates, computing the intersection in local coordinates, and then using the local transformation to convert intersection properties back into world coordinates (which is much easier).

Several pieces of advide when implementing this:

- Even if the ray direction starts off as a unit vector, if you apply the transformation to the ray and the transformation involves a scale change, then the direction of the transformed ray need no longer be a unit vector. (This means that the distance returned by the
RayShape::intersectmethod may be a scaled version of the true distance.) Make sure to normalize your ray direction after applying the transformation to ensure that it is unit-length!- Keep in mind that transforming the surface normal at the point of intersection from local coordinates to global coordinates requires applying the inverse transpose of the matrix transformation.
- Because you will be transforming the ray from local to global coordinates and vice versa many times, it makes sense to compute the inverses and or transposes once and then store them, rather than computing the matrix inverse for each ray. To help you with this, the code definese members
`inverseTransform`

and`normalTransform`

of the class StaticRayGroup for storing the necessary matrices. You should set these when the matrix is initialized in StaticRayGroup::set (`Ray/rayGroup.todo.cpp`

) and then use the calls RayGroup::getInverseMatrix and RayGroup::getNormalMatrix to get a copy of the matrices.- (1)
RayScene::GetColor(`Ray/rayScene.todo.cpp`

):

ImplementRayScene::Reflect(in`rayScene.todo.cpp`

) so that given the normal`n`

at a point of intersection, it returns the reflected direction of a ray hitting the surface with direction`v`

and modify the implementation of`RayScene::GetColor`

to recursively cast reflected rays from the point of intersection and add the reflected color contribution to returned color value.

(Be sure to modulate the reflected color contribution by the specularity of the surface.)- (1)
RayScene::GetColor(`Ray/rayScene.todo.cpp`

):

Recursively cast refracted rays through the point of intersection and add the refracted color contribution to returned color value, ignoring the refraction index for now. (Be sure to modulate the reflacted color contribution by the transparency of the surface.)- (2)
RayScene::GetColor(`Ray/rayScene.todo.cpp`

):

To allow transparent objects to cast partial shadows, implement:Then, replace the call to

RayPointLight::transparency(`Ray/rayPointLight.todo.cpp`

);RaySpotLight::transparency(`Ray/raySpotLight.todo.cpp`

); andRayDirectionalLight::transparency(`Ray/rayDirectionalLight.todo.cpp`

);RayLight::isInShadowwith a call toRayLight::transparencyto allow for partial shadowing.- (2)
RayScene::GetColor(`Ray/rayScene.todo.cpp`

):

ImplementRayScene::Refract(in`rayScene.todo.cpp`

) so that, given the index of refraction`ir`

of a the surface at an intersection point and given the normal`n`

at the point of intersection, it writes the refracted direction of a ray hitting the surface with direction`v`

into the vector`refract`

. The function should return a value of 0 if the ray could not be refracted (e.g. if computing the refraction direction would require evaluating the arcsin of a number with magnitude larger than 1 so that the angle of the ray is greater than the critical angle). Then, modify the implementation of`RayScene::GetColor`

so that it uses the correct direction for computing the color contribution from refracted rays.Accelerated Ray-Tracing(3) Accelerate ray intersection tests with hierarchical bounding boxes. To do this you will have to:

RayShape::setBoundingBox(`Ray/ray*.todo.cpp`

):

For each of theRayShapesubclasses that you have implemented (`RaySphere`

,`RayTriangle`

, etc.). This method will set the bounding box of the shape (RayShape::bBox) and return the bounding box.RayGroup::setBoundingBox(`Ray/rayGroup.todo.cpp`

):

To implement the`RayShape::getBoundingBox`

method for For a`RayGroup`

, accumulate the bounding boxes of all the childRayShapes, compute the bounding box of the transormed accumulation of bounding boxes, store and return it.

(Note: When the parser is done reading the .ray file it automatically calls theRayShape::setBoundingBoxmethod for the root node, so if you have implemented this method for all of the subclasses ofRayShape, the bounding boxes are already in place to be used for intersection queries, and you do not have to reset them.)BoundingBox3D::intersect(`Util/geometry.todo.cpp`

):

Return the distance along the ray to the nearest point of intersection with the interior of the bounding box. (If the ray starts off inside the bounding box, then a value of 0 should be returned.) If the ray does not intersect the bounding box, a negative value should be returned.RayGroup::intersect(`Ray/rayGroup.todo.cpp`

):

Modify this method to only test for an intersection or a ray with a child RayShape if the ray intersects the bounding box of the child. Keep in mind that if the value of the`mx`

parameter is greater than 0 and the distance to the intersection of the ray with a child's bounding box is great than`mx`

, you do not need to test for intersection with that child.

- (2)
RayGroup::intersect(`Ray/rayGroup.todo.cpp`

) Modify this method so that it first computes the intersection with all the bounding boxes of all the child shapes, then sorts the shapes based on the proximity of their bounding box intersection and finally, tests the child RayShapes in order, allowing for early termination of the ray tracing.

To optimize this method you may want to use the RayGroup::hits member to store the shapes and the distance to the intersection of their bounding boxes. (The size of this array is set to be equal to the number of children, so you needn't worry about memory allocation.) You can then use the staticRayShapeHit::Comparemethod for sorting the shapes by calling something akin to:

`qsort(hits,hitNum,sizeof(RayShapeHit),RayShapeHit::Compare);`

(Note that`hitNum`

can be smaller than the number of child nodes, as you only need to consider those shapes whose bounding boxes were intersected by the ray.)- (2)
RayTriangle::intersect(`Ray/rayTriangle.todo.cpp`

):

Modify this method so that it returns the texture coordinates at the point of intersection and modifyRayScene::GetColor(`Ray/rayScene.todo.cpp`

) to support texture mapping (with bilinear interpolation of texture samples). That is, instead of using theRayLight::getDiffusemethod to compute the diffuse color contribution, use the value read out from the texture map for the diffuse color.- (1)
RaySphere::intersect(Ray/raySphere.todo.cpp):

Modify this method so it returns the texture coordinates at the point of intersection (longitude and latitude).

(If you haven't already done so for the triangle, you will also need to modifyRayScene::GetColor(`Ray/rayScene.todo.cpp`

) to support texture mapping.)- (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.
- (1) Generate a 3D scene and save it as a .ray file. The scene should have both spheres and triangles, should have all three types of light sources in it, and should contain at least one primitive with a transparent material.
- (1) Treat
RayPointLights andRaySpotLights lights as having a finite 'area' and cast a collection of rays during shadow checking to generate soft shadows. That is, instead of casting just a single ray towards the light to compute the shadow component of the intersection point, cast multiple rays in the direction of the light and use the average value as the shadow components.- (1)
RayBox::intersect(`Ray/rayBox.todo.cpp`

):

Compute ray intersections with a box.- (1)
RayCylinder::intersect(`Ray/rayCylinder.todo.cpp`

):

Compute ray intersections with a cylinder.- (1)
RayCone::intersect(`Ray/rayCone.todo.cpp`

):

Compute ray intersections with a one.- (2) 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) Simulate the behavior of a
real camera lensby implementing the procedure in this SIGGRAPH paper.- (2)
Accelerate ray intersectionswith an octree or BSP spatial data structure.- (?) Impress us with something we hadn't considered...
30 points. In addition to implementing these features, there are several other ways to get more points:For images or movies that you submit, you also have to submit the sequence of commands used to created them, otherwise they will not be considered valid.

- (1) Submitting one or more images for the art contests.
- (1) Submitting one or more .ray files for the art contests.
- (2) winning the regular art contest,
- (2) winning the .ray file art contest,
- (?) Implementing an accelerated ray-tracer that can ray-trace a
verylarge model in very little time (e.g. a few minutes for a model consisting of hundreds of thousands of triangles, with a recursion depth of 5).It is possible to get more than 30 points. However, after 30 points, each point is divided by 2, and after 32 points, each point is divided by 4. If your raw score is 29, your final score will be 29. If the raw score is 33, you'll get 31.25. For a raw score of 36, you'll get 32.

Please include the following in your submission:

- the complete source code (with Makefile if compiling under Linux);
- any .ray files you created.
- images for the art contest
- a writeup.