\documentclass[letterpaper,10pt]{article} %\topmargin -1in %\textheight 10in %\oddsidemargin -0.25in %\evensidemargin 0in %\textwidth 6.5in \setlength{\parindent}{0pt} \setlength{\parskip}{1em} \pagestyle{empty} \usepackage{geometry} \geometry{papersize={216mm,279mm},total={176mm,239mm}} \usepackage{listings} \lstloadlanguages{C++} \title{CSC418 Assignment \#3} \author{Aly Merchant [991579083]\\Mohammed Ajmal [991579831]} \begin{document} \maketitle \section{Raytracer} This report summarizes the work we did for our final graphics project in CSC418. We chose to implement a raytracer for this assignment. We hope to summarize some of the design decisions that were made as well as the reasons that motivated them and we hope to highlight some of the aspects of our implementation. \section{Division of Labour} At a high level the division of work was as follows: Aly was responsible for all the lighting, reflectance and texture mapping in the ray tracer. Mohammed was responsible for the geometry of the project, including all shape classes along with the intersection routines for spheres, cylinders and planes. In addition, his tasks included implementing shadowing effects for the raytracer. It should be noted that although we have indicated a sharp line in terms of who did which tasks, in reality, this was not the case. Indeed, when it actually came time to implement the project, we both worked in close coordination with each other, with each person helping out the other as the need arose. Certainly, all the design decisions were made bilaterally. \section{Explanation of Design} When we first started this project, we had thought that since a lot of the code was already given to us, we would simply use that to further enhance the ray tracer. In the end, however, we chose to start from scratch. In fact, we may be one of the only groups that started with a clean code base. Part of the reason for this was just the desire to write everything ourselves, but also, we found that some of the code provided was not intuitive for us. Thus, we started developing the raytracer from the foundation upwards. The very first decision we made was to have an abstract Shape class that was easily extensible and provided placeholders for key functions (such as, for example, the "rayIntersectsMe" method. Since this was an abstract class (with pure virtual methods), each child class would override this method with its specific implementation of ray-object intersection. This made the design quite flexible. We could add objects as we needed, and at the same time, we were not stalled from continuing to more complicated effects even if we only have one primitive fully implemented. Next, we addressed the issue of scenes, specifically, how we would go about creating our scene. Once again, in the interest of some degree of innovation, we decided to create our own meta-language. The language is by no means complicated, yet it is more than adequate to capture a number of scenes as is evident through our image submissions. The language is based on the idea of {\em environments}. For example, one can open a "SPHERE" environment, and then provide a list of spheres, with each line correponding to one sphere. On this line, the user would list certain required properties of spheres, such as their centre and radius, but the user has the option to leave other properties unspecified. In this case, we would fill in the blanks. We found this worked quite well, we were able to pretty rapidly generate scenes we wanted. Another major benefit was that it aided with debugging the program. Since we had such fine control over where objects could be placed, etc. we were able to quickly isolate bugs by designing appropriate scenes. (For an example of scene file, see any *.sc file) One final design decision: We chose to use exclusively object-oriented programming for this project. We felt that this allowed for a cleaner implementation. Also, raytracers inherently lend themselves to OOP; it is very easy to think of concepts that can be converted into objects (example: ray, shapes, scenes, etc.) \section{Implementation Details} We chose to implement the raytracer in C++. The main competitors were C and Java, however, we really wanted to follow the OOP paradigm, and of course, C doesn't allow for this. Java sufferend from an efficiency problem, and with computationally intensive tasks such as raytracing, we didn't want to program in java. One of the first classes we wrote was the abstract Shape class which is found in shape.cc. This was to be the starting point for all other shapes. As previously mentioned, it was designed to be an abstract class, with pure virtual methods. Concurrently, we started work on our 3D vector class, Vec3D. This class was a bit different however, as it wasn't one large task. Rather, we added functions to the class as needed. Here, another feature of C++ proved to be of great use, namely operator overloading. You can see in the vec.h header file, that we overloaded a large number of operators to make the Vector class behave more intuitively. The implementation of the parser for our meta-language is very simple based on the restrictions that we placed on the language, and this was done next, so that we could start testing the algorithms. The first shape we created was a sphere, practically the "Hello World" of raytracing. In the interest of brevity, we won't go into specific details on how we wrote the intersect methods for each of the shapes. Suffice it to say, the intersection code was written in the aforementioned "RayIntersectsMe" method. This would take in a ray, and would compute if the ray intersected itself (itself being a Shape subclass). For shadows, we followed the technique outlined in class. That is, once we find a ray which intersects a shape, we project a ray from all light sources to that point. If light from some source reaches that point, then it is illuminated. Rather than create a complicated texture mapping pattern, we used a trick that we learned some years ago, the so-called XOR pattern. This pattern was created by using the x and y components of the surface normal at a point. In the image we supplied named "textures.png", it is this pattern that you see appearing on the spheres. Finally, we also implemented reflections. Once again, we followed the algorithm outlined in class, using a recursive raytracing algorithm, with a suitable maximum recursion depth. Those are some of the key points regarding the implementation of this project. \section{Code Listing} \lstset{numbers=left,stepnumber=5,numberstyle=\tiny,numbersep=5pt,breaklines=true} \lstinputlisting[language=c++,tabsize=2]{shape.h} \lstinputlisting[language=c++,tabsize=2]{shape.cc} \lstinputlisting[language=c++,tabsize=2]{scene.h} \lstinputlisting[language=c++,tabsize=2]{scene.cc} \lstinputlisting[language=c++,tabsize=2]{raytrace.cc} \lstinputlisting[language=c++,tabsize=2]{vec.h} \lstinputlisting[language=c++,tabsize=2]{vec.cc} \end{document}