CS 315 Homework 6 - Ray Tracer
Due Date: Tues Nov 26 at 11:59pm
Overview
In this assignment, you will write a simple ray tracer. Like rasterization, ray tracing is an algorithm for rendering 3D scenes into 2D images. However, ray tracing can produce some amazing visual effects that rasterization has difficulty with: such as accurate shadows, mirror reflection, and refractions/transparencies. When you've finished with this assignment, you'll have produced a piece of software (almost entirely on your own) that can generate images such as these:
In the end, your ray tracer will be able to:
- Render spheres and triangles (and by extension, meshes made of triangles!)
- Perform basic shading using the Phong illumination model
- Include lighting from ambient, point, and directional light sources
- Capture shadows cast by objects that occlude light sources
- Calculate mirror reflections
If you wish, you may also include further effects: texture mapping, depth of field, soft shadows, etc.
Using these effects, you should render a scene that contains at least 3 objects, all of which cast shadows and at least one of which has mirror reflectance. You'll need to include at least one sphere and one triangle. Note that a Cornell Box is a nice way of demonstrating your ray tracer, such as by making an image similar to the right image above.
This assignment should be completed individually. You are encouraged to discuss high-level issues with other students, but your code must be your own.
Objectives
-
Be able to implement the mathematics and algorithms of ray tracing
- (Including light, shadows, and reflections
- Practice applying lighting models, such as with the Phong illumination model
- Apply object-oriented programming principles to graphics
- Consider the runtime speed of graphical algorithms
Necessary Files
Since ray tracing involves its own graphical pipeline, we won't be using OpenGL any more. This means that the code you're writing is effectively just Java code. While this can be run on Android emulators and devices, such hardware is much less efficient than the Java runtime on a laptop or desktop. And since the code is (basically) identical, why implement and test the code on Android (which may be 100x slower) instead of in straight Java?
For this reason, the starter code you should download and extract contains two different starter code sets.
-
One is an Android project like you're used to seeing--this includes a
RayTracerActivity
that will allow you to render to aSurfaceView
(like you may have done in Hwk1). The other is a Java project that includes aRayTracerWindow
class you can run to have your rendering appear in a classicJFrame
. -
Aside from these windowing programs, each project contains a copy of
RayTracer.java
. This will be your "starting point" for writing your ray tracing program. These classes are almost identical--except one renders to aBitmap
and the other renders to aBufferedImage
. So apart from that minor refactoring (and a few extra pieces), the code base is the same.
Beyond those two classes, everything else in the starter projects is identical. This is because they in fact run the same code!
Thus is should be trivial to take code written in one project and "port" it into the other.
As part of the assignment, you will be porting the code from one platform to the other. This will involve copying most of the Java files, tweaking the contents of the RayTracer
class... and that's it!
I have also included the following classes to help get your program started:
-
Ray:
A class that starts the representation of a ray that you will be tracing. As we discussed in class, Rays have an origin and a direction. -
Camera:
A class to represent the camera used in the rendering. This class contains (unoptimized) logic to produce the view and projection transformations that are necessary for calculating view rays -
Vector3d:
A class of utility methods for working with vectors (represented asdouble[]
s). Note that there may be other more efficient libraries or implementations--for example creatingVector3d
objects you can add and subtract may make your life easier- After using this for a bit, you'll start to miss the simplicity of GLSL's
vec3
type!
- After using this for a bit, you'll start to miss the simplicity of GLSL's
Finally, as always, the zip file also contains a README.txt
file that you will need to fill out.
Other Resources
I have also made available a few other resources you might find useful. In particular, here is a a set of
example scenes
that you can use to test your ray tracing program as you get it developed. Each scene is described in a text file, the format of which is detailed in the included SceneFileFormat.pdf
file. There is also a rendering of what the scene should look like. Note that these renderings were produced with a different program, so you might not get the exact same output.
- Writing a parser for the scene file format would be worth extra credit :)
Recommended Design Strategy
While ray tracing is a relatively simple algorithm conceptually, it requires a non-trivial amount of code to implement (because you need to do all the work yourself). If organized poorly, the program can get out of hand in a hurry. Fortunately, the ray tracing algorithm is a good target for object-oriented software design. Below is a list of classes that a ray tracer might be broken in. You can think of these as conceptual classes--ways or organizing data that may or may not have a 1:1 correspondence with Java classes. You are welcome to use or ignore these suggestions as you see fit.
-
Scene
A class that may organize your entire rendered scene. This could store the camera, the image plane, and all the lights, materials, and surfaces in the scene. A Scene might have a method for rendering itself. Note that the providedRayTracer
class may do some of this work. -
Camera
A class representing the virtual scene camera. It tracks location and facing of the camera, and may be responsible for generating viewing rays. Hey, I think this class might already exist... -
Ray
A class representing a ray blasting through space. Remember rays are defined by the equationa + d*t
. Rays might also have information about a minimum and maximum t value for which interactions are considered valid. I think this class might also already exist... weird. -
Surface
A class representing a surface that rays can intersect with. Surfaces might keep track of theirMaterials
, or perhaps you will wrap your surface geometry and and materials in a broaderSceneObject
class. Note thatSpheres
andTriangles
would be specific instances of aSurface
. In fact, you could make good use of polymorphism here (such as by makingSurface
an abstract class!) -
Sphere
A class representing a sphere. Spheres are easy to intersect with! -
Triangle
A class represent a triangle. Triangles are slightly more complicated to intersect with (but refer to the lecture nodes for pseudocode). -
Material
A class that represents the ambient, diffuse, specular, and shininess properties of a material (or an object). You might have this class do shading computations: given an intersection point, a normal, a viewing direction, and a list of lights in the scene, what color is the material? On the other hand, some of those details more properly belong to the Scene class... -
Intersection
A class that represents an intersection between a ray and a surface. This could store information about that intersection, such as the t value it occured at, the intersection point, the normal value, etc. -
Light
A class that represents a light source. It should be able to, at least provide the direction of the emitted light to the surface point (or vice versa). You may want to have subclasses for different kinds of lights:AmbientLight
s,PointLight
s, andDirectionalLight
s. Note that ambient lights behave a bit differently from others.
Development Schedule
Get started on this right away! This is in some ways a smaller assignment than the last (less moving parts), but still has a significant number of components. Luckily, each component can be more easily tested in isolation--and since you can develop in Java, the debugging process should be quicker.
I recommend creating your program through the following steps:
-
Start by adding in a few Spheres and making sure you can intersect with them. The sphere (and camera!) locations from the exampleScenes are good to use so you know if your Spheres are in view or not. To start, don't worry about calculating shading: just return a constant color if the ray intersects a sphere (imagine 100% ambient light). Try tweaking the camera and such to make sure that things make sense.
This is a good target for Thurs 11/14 -
Now that you have intersections, you can implement basic lighting and shading. This will involve transfering the logic and understanding from your last homework into your ray tracer. Get your spheres to become shaded and have specular highlights!
This is a good target for Sun 11/17 (Saturday really, but who are we kidding?) -
Add triangles to your scene and make sure you can intersect and render them correctly. Note that you shouldn't have to change your lighting code, simply add another class with its own intersection function. Polymorphism will be your friend here!
This is a good target for Tues 11/20 -
Add in shadows to your program. This will involve casting new shadow rays. You may need to play with the "shadow bias" parameter if you notice any black speckles or shadow acne.
This is a good target for Thurs 11/22 -
Finally, add in recursive reflections so you can get mirror effects. Experiment with different bounce depths, and how it effects the speed of your program.
This is a good target for Mon 11/25. That leaves you another day to finish things up before the assignment is due. - Your last step should be to "port" your code into the Android project. Be sure to test that everything works. See how long it takes for your scene(s) to render! You are expected to turn in both completed projects.
Extensions
This is another somewhat open-ended assignment, so has lots of possible extensions. Up to 10 points of extra credit are available for extensions such as:
- Distribution ray tracing effects: Generate multiple viewing rays per pixel for anti- aliasing, soft shadows, motion blur, and depth-of-field defocus blur.
- Object transformations You might add in the ability to apply transformations (scales, rotations, etc) to objects. Basically you just apply the inverse transformation to your ray to see what it intersects (jumping between coordinate spaces)
- Additional light types You might try implementing spot lights, area lights, or lighting from environment maps.
- Additional primitive types: It's easy to extend a ray tracer by adding different geometric primitives, such as triangle meshes (but watch out for speed!), quadratic surfaces, or implicit surfaces. All you need to do is figure out how you know when you've intersected the surface, and what the normal is at that intersection point!
- Acceleration structures: Add in bounding volume hierarchies (such as axis-aligned bounding boxes) or space partitioning trees to speed up your program. This will help you to efficiently render scenes with a large number of primitives.
- Non-linear perspective: Your ray tracer generates images by sending viewing rays through a planar imaging surface, but the imaging surface doesn't have to be a plane. If you replace it with a hemisphere, for instance, you can get a cool fish-eye lens effect. There are other nifty imaging surfaces you can play with, such as cylinders.
Submitting
BEFORE YOU SUBMIT: make sure your code is fully functional, but also documented and readable. Again, grading will be based primarily on functionality. I cannot promise that I can find any errors in your code that keep it from running, so make sure it works. But I will look over your code to check your implementation, so please make sure that is readable :)
Upload both the Java and Android projects (including all the src, resources, layouts, libraries, etc) to the Hwk6 submission folder on hedwig. Also include a filled-in copy of the README.txt file in your project directory!
The homework is due at midnight on Tues, Nov 26.
Grading
This assignment will be graded based on approximately the following criteria:
- Generates a raytraced scene with the appropriate objects [10%]
- Calculates intersection and shading for spheres [15%]
- Calculates intersection and shading for triangles [15%]
- Applies Phong illutmination for objects [15%]
- Includes shadows for objects [15%]
- Includes recursive mirror reflections for objects [15%]
- Ray tracer runs in both Java and Android [5%]
- Code is documented and readable [5%]
- README is completed [5%]