CS11 Advanced C++ Lab 2 - Raytracer Kickoff


Ray Tracer!

This term we will go through the process of writing a simple ray tracer. Ray tracers happen to be quite well-suited to implementing in C++, and we will have plenty of opportunities to leverage many of C++'s more interesting features. Also, a larger project like this will give us opportunities to apply other widely used techniques, such as automating the build process and doc-generation, and using a version control system to manage our source code.

This week's lab is pretty simple - you will build a few basic classes that will be central to your ray tracer's operation. Getting these classes nailed down now will allow you to concentrate on the higher level tasks you will be implementing in following weeks. So, it is essential to build good, clean abstractions from the beginning.

One of the nicer features of C++ is that we can specify meanings for the various arithmetic operators we might want to use with our own classes. Although a simplistic approach is to implement operator overloads as member functions on our classes, this isn't always the best idea, and in fact it can be very limiting in certain situations. This lab will also give you an opportunity to practice the operator-overloading best practices discussed in class.

Vectors

The vast majority of operations within a ray tracer rely on 3D vectors. The direction of a ray, the surface normal at a particular location on an object, the location of a light in the scene, all of these things and more are represented as vectors. So, one of the first things we will need for our ray tracer is a complete, fully featured implementation of a vector data type.

Vectors are a pretty simple data type, and the math operations that can be performed on vectors are also pretty simple to implement. Since we know that our vectors are all going to be 3D, we don't need to dynamically allocate memory for the element values; we can just use a statically sized array of three values. Dynamic memory management in such a critical piece of functionality would destroy performance, so static sizing is the way to go.

Vector elements need to be floating-point values of some sort. It would probably be best to use float, since this will be both faster and more space-efficient, but if you want to go all out, you can represent coordinates as double values.

Create a class to represent 3D vectors, as outlined above. Make sure to provide a 3-argument constructor for initializing all the elements to specific values, as well as a default constructor that initializes all elements to 0. Of course, copy-construction and copy assignment are both good operations to have too, but C++ will provide them for you automatically. Since your array is fixed-size and not dynamically allocated, the compiler will generate correct versions of these by default.

Coding Style

Since this is going to be a central component of your ray tracer, you should make extra efforts to ensure that the code follows good style, is well commented, and that it uses assertions (defined in the <cassert> header) everywhere that inputs or outputs need to be validated. Make sure to do this diligently, so that you can avoid having to do any rework on this lab. This is a requirement for passing this assignment.

Make sure that all classes, every data-member and member-function of each class, and every non-member function are commented. You don't have to give tons of details; just state clearly what each variable or operation is for. Keep in mind that these comments will be used for auto-generated API documentation in a future assignment.

Other Operations to Implement

Here are other operations that your vector class should support. Make sure to follow the operator-overload guidelines discussed in class.

Finally, you need to provide several other operations, but these won't be implemented as operator overloads. The rationale for this is given below.

There will be more vector math operations to implement in following weeks, but this should be sufficient for now.

Colors

Your ray tracer will also need a class to represent colors, since the whole point of ray tracing is to determine the color of each pixel in the image. Create another class to represent colors in the RGB color-space. That is, each color will have a red component, a green component, and a blue component. Each color value should be represented by a floating-point number. (Again, we recommend float, but you can use double if you don't care as much about efficiency.) Normally these values will be between 0 and 1, but in some cases they may go outside that range when particular values are being computed.

For data members, you might want to create an array of three floats or doubles, like before, but you might find it clearer to create three separate data-members named "red", "green" and "blue". Do what is clearest to you, but document what you do. For example, if you use an array of values, document what color component is at each array index.

Here are the operations that your color class should support:

Testing Your Code

You really don't need to test your code this week, beyond making sure that it compiles. Your code will get enough of a workout in the following weeks! But, feel free to write some simple test code to make sure your vector and color classes work properly. For example, you might want to try your cross-product code to make sure it's correct. A very simple test would be that (1, 0, 0) × (0, 1, 0) produces (0, 0, 1). And feel free to write more complicated tests as well.

Extra Credit

Here are some additional things you can do if you want to make your data types even cooler:


Last updated January 23, 2008. Copyright (C) 2007-2008, California Institute of Technology.