In this assignment, you will implement path tracing to create photorealistic renderings of 3D scenes with global illumination. No starter code will be provided. Like in Assignment 1, you will compute the rendered image yourself on the CPU and then display it — only this time, you will do it using path tracing instead of rasterization.
Start by implementing a very basic ray tracer. For now, define a scene to be a list of spheres with specified centers and radii (we will change this later). Assume the camera is located at the origin and pointed along the −z direction with a fixed vertical angle of view (choose something between 40° and 90°), so that you can compute the ray o + t d for any pixel location. Intersect the ray with the scene, find the normal n of the nearest hit point, and set the pixel colour to be ½ (n + (1,1,1)), or (0,0,0) if nothing was hit. Even without doing any lighting calculations, this will allow you to see the shape of the objects in the scene. (Once you’ve computed the entire image, you will of course have to open a window to display it, and/or save it to a file.)
Modify your scene representation to allow other shapes to be included, namely, infinite planes and axis-aligned boxes. (You can also implement other shapes if you want.) A natural way to do this is to make all these shapes subclasses of an abstract “shape” class. Each concrete shape should provide its own ray intersection function that returns the nearest valid intersection of the given ray with it. Replace the second sphere with the plane y = −1 and add some boxes to the scene to verify that your new shapes are working correctly.
Add some lighting. Modify the scene representation to also have a list of light sources (assume point sources with specified intensity in RGB for now), and give each shape a diffuse albedo as an RGB triple. For each pixel, set the colour to be the radiance going towards the camera based on the Lambertian model, adding up the contribution of each visible light source.
Optional: (Correction: not optional)
Add a viewing transformation that allows you to move and rotate the
camera. As discussed in one of the earlier lectures, you can do this by
specifying the center of projection, the view vector, and the up vector,
and using them to construct an affine transformation matrix. Also add
affine transformation matrices to each shape so that you can move,
rotate, and scale them.