# FART (FART Ain't A RayTracer)

Project Description Code Design Document Screenshots Final Report

## FART Design Document

1. Overview

FART is my semester project for the Computer Science 658 class at Grand Valley State University. My goal is for FART to be a distributed, object-oriented ray-tracer. The name is a recursive acronym similar to "GNU" or "LAME."

FART will take in a scene-description file in a text-based input file format. After the scene file is parsed, it will be raytraced (rendered) according to the render options. Some of these options can be specified on the command line and others can be specified in the scene description file. When a given option can be specified both places, the command-line options will override what is in the scene file.

2. Distribution

Rendering can be done using a built-in distributed algorithm. Command-line options will be utilized to specify a "hosts file" which can contain a list of hosts to use while rendering the scene. The algorithm will be fault-tolerant so that if one of the worker nodes goes down the work can be reorganized to complete that section of the image.

The instance of the program which is directly invoked by the user will be the "master" process. If a hosts file is specified as an argument to the program, then the master process will read a list of hosts to use as worker nodes. It will spawn an instance of the program on each worker node, and dole out chunks of the image to each worker node in a master/slave, client/server manner.

Since the amount of time to render a given chunk of the image can vary tremendously depending on the number of objects in the scene, the maximum recursion depth, and the antialiasing level, the master process does not know how long it should wait to expect a reply from the worker nodes. Therefore, a thread on the master node will be dedicated to polling all the worker nodes at an interval of some number of seconds. The worker nodes should respond to this polling message with a "still working" message to indicate that they are still up and are processing data. If a given worker node does not reply to the polling message, it will be considered "down" and the work it was processing will be doled out to another node.

Update: Instead of having a separate thread from the master poll the slave nodes, a more straightforward solution was implemented. When a slave node requests a task from the master, the master will respond with the next available task and record that this task is in progress. If a request comes in for a task when all of the initial tasks have been exhausted, but there are some in progress, the master will respond with one of the tasks that is marked in progress for the slave node to work on. In this way, if the original slave node working on the task does not respond and finish the task, the new slave node should respond and complete the task. This method also has the advantage that it will work whether a slave node is extremely slow or truly unresponsive. The master will use the data from whichever node responds first announcing the completion of a given task.

In this way, the raytracer program will be fault-tolerant. It can tolerate each (Update: all but one) of the worker nodes going down and still produce the final image.

Update: To implement this distributed algorithm, I thought about using the MPI framework. MPI, however, was not natively designed to handle fault tolerance. So, I decided to implement the distributed framework with raw TCP sockets. This solution is very portable because it only relies on TCP socket availability and not a third-party library.

At first TCP was not sending the message data fast enough for my program. I considered switching to UDP but instead turned on the socket option TCP_NODELAY which disables the "Nagle" buffering algorithm and sends the data as soon as it is available. This made using TCP feasible and my CPU utilization grew.

The application-level protocol is very simple. It was designed to transmit short messages across the wire. An integer representing the message type is transmitted, followed by the payload of the message. Task identifiers are sent using a single 4-byte integer. Bitmap data is transmitted as packed data, 3 bytes per pixel.

3. Program Organization

The program follows the object-oriented design paradigm. It is written in C++ so that it is portable and efficient. It is made up of a collection of modules, which are organized into functional groupings. Each module implements one or more classes of objects for the system. The groupings of modules are utility modules, scene description file parsing modules, shape modules, program distribution modules, and main program modules.

4. Class Diagram

5. Ray-Tracing Algorithm

The actual scene will be rendered by tracing rays from the virtual camera's position in space through a virtual plane perpendicular to the view direction which represents the image being rendered. For each pixel, a total of m^2 rays will be launched, where m is the multi-sample level set by the user. Setting a multi-sample level greater than 1 effectively produces an anti-aliased image.

Rays are collided with objects in the scene, and the closest object to the viewer that is hit by the ray is recorded. When this object has been found, its material properties are examined for lighting information, reflection levels, and transparency levels. Update: If the object found is a boolean object, made up of multiple sub-objects, then the material properties of the actual sub-object at the intersection location are what are used for lighting, etc...

Unless the object is completely transparent or 100% reflective, for each light in the scene, a ray is launched from the contact point on the surface of the closest object to the light source. If the ray hits the light source then the Phong lighting model is computed to calculate the color for that point on the surface. If the ray instead hits another object instead of the light source, then that light source does not add to the color at that point, effectively producing a shadow.

After calculating the light source's contributions to the color, the algorithm will recurse if the surface has non-zero reflectivity or transparency. This allows the scene to accurately be drawn from a reflected point of view from a particular surface. This is one of the major strengths of the ray-tracing algorithm.

When the scene attempts to determine if a given ray collides with a shape, it calls the virtual function intersect() on the shape. This function is overridden in all shape objects. It returns a list of (shape, point) intersection pairs telling which shape was hit and which point it was hit at. Update: The intersect() method actually returns three values: The actual shape hit, the intersection point, and also the surface normal at the intersection point. The surface normal is returned because it may not be equivalent to the surface normal of the actual shape hit. The reason for this is the Subtract boolean object type. When one object is subtracted from another (in the form A - B), the effective surface normal of the composite object at a point on the edge of object B will be the inverse of the true surface normal on object B. The reason it returns the shape hit is because the shape object inquired may be a composite object of some sort (such as an intersection, union, or subtraction of sub-shapes). Thus, the caller knows which actual shape object was hit and can thus use this shape object to determine material properties and the surface normal at the intersection point.