Tuesday 19 July 2011

Concluding Remarks

The course is nearly finished and my ray tracer is nearly done. I did not get around to completing the spacial subdivision objective, so that means a maximum of 9/10 for the objectives. It's not a perfect program, and it definitely has some quirks, but I sure am proud of what I've accomplished. I've done my best to polish it up into a decent, user-friendly application. I hope my demo goes well!

Final Scene

My final scene consists of some marble pillars near some trees with mountains in the background. It is complete with a shining moon and a starry sky.
Here's the original image with no anti-aliasing:


And once more with anti-aliasing:



I ran it one more time after I implemented simple shadows (finally) but for some reason, I get funny lines on the right side of the screen. If it weren't for those, it would look much cooler.


There's also the slight anomaly of the moon casting a shadow on the sky. It's kind of silly, but I guess it gives the scene a sort of 'diorama' look?
I'm not sure how "interesting" it is, but I like it. It makes heavy use of texture mapping, cones and cylinders. Every object is texture mapped.

Multi-threading

This was probably the most frustrating objective to complete... and I'm not all that sure it's complete. The program now allows the user to chose 1, 2, 4 or 8 threads to render the image. This speeds up the rendering time dramatically, but only if your computer has at least a number of cores matching the number of threads selected.
Below is a graph showing the time it takes to render a simple 256x256 image with 5x5 grid sampling:



This was tested on a 4-core machine, so there are no results for 8 threads. It's pretty obvious that more threads is awesome.
A fun byproduct of mutli-threading is that you can see different sections of the image partly drawn when you cancel a render. Here's an example of an image partially drawn with 4 threads:



A not-so-fun byproduct of multi-threading the unreliability. It's difficult to make a thread-safe program. My ray tracer has some undiscovered threading issues, so it will occasionally seg-fault. Ah well. I did my best.

Fish Eye (Wide Angle) Lens

The "fish eye" or wide angle lens allows an image show an increased field of view by projecting the image into a sphere. This is best explained with a picture:


Here we see a sphere in a box. There are some other primitives, but they are far off to side and we can only glimpse a corner of each.
When we use the fish eye lens, we get this:


Suddenly the shapes that were to far outside the field of view are now visible. Note that the sphere looks perfectly normal, although farther away, while the objects on the sides look curved around the edges of a sphere. I added a small fade to black around the edges for effect. The above image was rendered with 40 degrees field of view. This following one is the same image, only with 80 degrees field of view:

Depth of Field

Depth of field is the added effect of focus. By focusing on a particular distance, objects nearer and farther away begin to blur. This is done simply was distributed ray tracing and adding a "disturbance" to each ray. Each rays point of origin
is shifted slightly based on the disturbance, which is a random offset. The offset increased as the distance between the destination of the ray and point of focus increases. Since the destination is required to compute the disturbance, depth of field takes longer to render than an image with no focal point, since the destination must be calculated beforehand.
Here is a simple image with a bunch of spheres at various depths:


Now, here is the same image, only focusing on the nearest red ball:



Same image again focusing on the green ball in the middle:

Same image once more focusing on the farthest white ball:

Texture Mapping

Texture mapping was another objective that seemed complicated at first. The idea is to map the point on an object in 3-space to a 2-dimensional image and colour the point with the corresponding pixel in the image. I started with spheres, and again, it took a long time to actually get the math right.
After spheres, I did planes, which is the easiest primitive to map onto.  Simply repeat the texture over and over again from a starting point out.
Other primitives where giving me trouble. Cones in particular were tricky. Then I discovered sphere-mapping. This is a method of texture mapping analogous to shrink wrapping. Simply project the texture onto a sphere, and then map the sphere onto any object using the radial distance from the center. The same method works for any bounded primitive (i.e. not planes).
Here is an example of texture mapping. A world map is texture-mapped on a cylinder and a box. The cone has the texture of a pine tree, and the sphere has a moon texture. The plane is textured with grass.

Cel-Shading

Cel-shading has been my favorite objective. It was the 3rd one I actually completed and I really like the way it looks. The method is fairly simple. Colours are flattened out into a set number of intensities based on the normal of the light hitting an object. This provides the impression of colouring objects in with a pencil crayon using flat shading (no gradients).
To get the black lines is a little trickier. A temporary image is first generated. This image colours each object with a flat shade of grey. Each object is given a different shade. Then, using the Sobel operator, a change in intensity is measured at each pixel. If the intensity of a colour changes, that means the edge of an object is at that pixel.
A giant grid of 1s and 0z is then produced. A 1 means there is no edge at the corresponding pixel and a 0 means there is an edge. When the actual image is rendered, each pixel colour is multiplied by it's corresponding grid in the cell. So, if there is an edge at a particular pixel, it will be multiplied by 0, resulting in the colour black, otherwise the calculated colour is used.
The following is a simple image rendered normally:


Here, we have the same image cel-shaded!

Anti-Aliasing

From what I learned in class, there are a few ways to perform anti-aliasing and they all involved super-sampling, or shooting more than one ray at a particular region and averaging the resulting colours. My tracer has the ability to render with no anti-aliasing, grid sampling, random/stochastic sampling, or jitter sampling.
Grid sampling fires one ray at each cell of a pixel divided into a regular grid. Random sampling fires a bunch of rays at the pixel randomly. Jittering combines the two by firing one ray at each cell of the pixel, but it's position in the cell is random.
Beyond the distribution method, the program provides an option of sample sizes. Sample size determines the number of rays that are fired at each pixel. The sample sizes range from 3x3 to 11x11.
To illustrate, here is a simple image with no anti-aliasing:


Now here is the same image rendered with a 3x3 grid distribution. You can see the edges of the spheres are smoother (you might have to click on the image to zoom in and see the difference).
And finally, the same image with an 11x11 jitter distribution. It is smoother than the above image, but not by much. It also takes forever to render.

Cylinders and Cones (and Planes)

Making cylinders and cones turned out to be a bit more frustrating than I thought.
I'm not sure why, but I struggled a lot with the math for a cone of various heights. In the end, it all worked out, and I now have 5 primitives: spheres, boxes, cones, cylinders and planes. I threw in planes because they were easy and make simple and effective backgrounds.
Here's an image showing all the primitives:

The GUI

The first objective tackled was the GUI... sort of.  A basic GUI was made at the beginning, but it evolved as I complete the other objectives. The final product looks something like this:


This is what you see when you start up the program.  The menu you bar at the top gives you access to all the features which you can toggle on or off.  You can also open a lua file, which describes a scene, from there. There's (obviously) also the option to start rendering an opened file as well as the option to cancel a render in progress.
Under the menu is a label that displays some info about the current state. The currently opened file is shown as well as the image size to be rendered, the ray distribution method used (more on that later), whether or not multi-threading is on, and a list of other features that are turned on. The state of the current render is also displayed.
Below the label is a giant void to be filled in with the image when render is complete.  After running the program with a simple image describing some spheres, it looks like this:

About the Course and Project

I am currently enrolled in CS488, Computer Graphics, at the University of Waterloo. The final project of this course is a self directed project based around 10 different graphics-related objectives. I have choses to add new features to the ray tracer that I made for a previous assignment. As this is a very work-heavy course, my previous ray tracer was never finished, so my project will be lacking a few simple things (mainly phong shading and hierarchical modeling).
The objectives that I have chosen are as follows:

1. Create a GUI for the ray tracer where all features can be turned on/off or varied.

2. Implement aliasing using super sampling.

3. Implement cel-shading.

4. Implement texture mapping.

5. Implement depth of field.

6. Implement fish eye (wide angle) lens.

7. Implement the ability to render additional primitives (mainly cones and cylinders).

8. Implement spacial subdivision.

9. Allow multi-threading.

10. Provide an interesting final scene.

Introduction

This is more or less a place holder post.  I have all my images (to be uploaded in later posts), and I'm adding some finishing touches to my ray tracer.
My plan is as follows, over the next day or two, add a bunch of new posts, each post outlining a single objective for my ray tracer.  Before that, though, I will post some info about the ray tracer in general, including some info about the course and why I am even making a ray tracer to begin with. I will also provide the list of objectives.