Part 1: Basic Ray Tracer

In the first assignment, we are expected to implement a basic ray tracer which supports the following features:

1. XML Parsing  & Storing a Scene:

      A Scene is provided to the ray tracer as input, which is defined as an XML file. So, the first thing to do is parsing this input XML file and storing the Scene in a convenient way. For XML parsing task, an external library called TinyXML-2 is utilized. For now, a Scene consists of Cameras, Point Lights, Materials, Objects and their related Vertex Data.

2. Simple Perspective Camera Model:

      For the first version of the ray tracer, only the simple perspective camera model is supported. In the input files, it is not guaranteed that the camera vectors are provided as orthogonal unit vectors, so they are converted to orthogonal unit vectors by the ray tracer to make sure that the camera parameters are set correctly.

3. Ray-Object Intersections:

      Currently, there are 3 types of objects which are Spheres, Triangles and Meshes where a mesh consists of a set of triangles. Ray-object intersections are performed by casting rays from camera to every pixel on the image plane. While intersecting rays with objects in the scene, it is important to take the closest intersection into account for the associated pixel, since the objects behind will not be seen by the camera.

4. Shading Models:

      In this version of the ray tracer, Ambient Shading, Diffuse Shading and Specular (Blinn-Phong) Shading are supported. Shading is used for determining the color of a pixel under the lights existing in the scene, depending on the material properties of the object.

5. Shadows:

      A point can be in shadow with respect to a light source if an object lies between the light source and the point. To decide whether a point is in shadow or not, shadow rays are casted from the point to the light sources.

6. Writing Scene to PNG File:

      Once the RGB values are calculated for each pixel, the only job left is to write these values to a PNG file. For this task, an external library called lodePNG is used.

Environment

The ray tracer is developed in C++ language in an Object Oriented way using Visual Studio Professional 2012. The machine environment is as follows:

OS: Microsoft Windows 10 64-bit
Processor: Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz
RAM: 16GB

Performance and Multi-threading

In this version of the ray tracer, no acceleration structures are utilized. However, since the rendering of the images takes a long time, I tried to speed things up a little bit by enabling the compiler optimizations of Visual Studio (/O2 flag) and introducing multi-threading. Since for each pixel the computations are performed independently, parallelizing the calculations was not so hard. I simply partitioned the image into equal parts horizontally so that each thread works on a number of rows individually. Maybe this is not the smartest way to perform multi-threading, but it helps to reduce the rendering times significantly.

For efficiency reasons, I have precomputed the camera and image plane parameters such as the top left corner of the image plane, since they remain constant for the entire process. Additionally, I computed the normals of the triangles while creating the triangles instead of computing them each time a ray intersects with the triangle.

Problems

While implementing the ray tracer, the most time consuming problem I encountered has produced the following buggy result.

Since the square object is a mesh which is composed of two triangles, I could not understand what caused it to turn out as expected while the little triangle's color is not correct. Since the square is fine, for a long time I thought that the problem was in the shading computations rather than the triangle creation part, and I checked the formulas over and over again. After many hours, I finally managed to realize that I forgot to normalize the normal vectors of the triangles. This has been a little hard to detect for me, because I was computing the normals of the triangles while creating the triangles as mentioned above.

Resulting Images

simple.png (800x800)
rendered on 4 threads: 1 second 624 milliseconds 668 microseconds
rendered on 1 thread: 2 seconds 5 milliseconds 539 microseconds


cornellbox.png (800x800)
rendered on 4 threads: 14 seconds 29 milliseconds 465 microseconds
rendered on 1 thread: 15 seconds 946 milliseconds 590 microseconds

For the cornellbox image, the edge where the blue and gray triangles meet and the edge where the red and gray triangles meet seem a little bit noisy. I tried different epsilon values for the intersection tests, but this was the best result I could obtain.

bunny.png (512x512)
rendered on 4 threads: 37 seconds 198 milliseconds 931 microseconds
rendered on 1 thread: 1 minute 39 seconds 701 milliseconds 717 microseconds

spheres.png (720x720)
rendered on 4 threads: 249 milliseconds 941 microseconds
rendered on 1 thread: 780 milliseconds 97 microseconds

two_spheres.png (800x800)
rendered on 4 threads: 46 milliseconds 854 microseconds
rendered on 1 thread: 100 milliseconds 689 microseconds

scienceTree.png (1440x720)
rendered on 4 threads: 2 minutes 19 seconds 964 milliseconds 394 microseconds
rendered on 1 thread: 5 minutes 35 seconds 228 milliseconds 245 microseconds

The scenes below are provided towards the latest days of the homework period. Thus, I could not obtain the timing results for some of the images when rendered on 1 thread, but the timing results when rendered on 4 threads are all provided below the images. Even with 4 threads, rendering these newly provided scenes took much longer than the previous ones, because the number of primitives they have are much more than the previous ones. Also, they have higher resolution compared to the previous scenes. Hopefully, in the upcoming weeks, some acceleration techniques will be introduced to this basic ray tracer and the timing results will be much better.

berserker.png (768x1024)
rendered on 4 threads: 4 minutes 34 seconds 863 milliseconds 154 microseconds
rendered on 1 thread: 15 minutes 30 seconds 485 milliseconds 908 microseconds

car.png (1024x768)
rendered on 4 threads: 13 minutes 33 seconds 100 milliseconds 924 microseconds

car_front.png (1024x768)
rendered on 4 threads: 12 minutes 56 seconds 139 milliseconds 320 microseconds

low_poly_scene.png (1024x1024
)
rendered on 4 threads: 12 minutes 52 seconds 942 milliseconds 708 microseconds
rendered on 1 thread: 1 hour 4 minutes 37 seconds 822 milliseconds 548 microseconds

tower.png (1080x1920)
rendered on 4 threads: 9 minutes 44 seconds 83 milliseconds 148 microseconds
rendered on 1 thread: 30 minutes 8 seconds 123 milliseconds 218 microseconds

windmill.png (800x800)
rendered on 4 threads: 6 minutes 26 seconds 758 milliseconds 462 microseconds
rendered on 1 thread: 16 minutes 23 seconds 864 milliseconds 711 microseconds


Comments

Popular posts from this blog

Part 3: Transformations, Instancing, Multisampling & Distribution

Part 2: Recursive Ray Tracing & Bounding Volume Hierarchy

Part 5: HDR Imaging & Advanced Lighting