Assignment 4 - Ray Tracer

Click here to jump straight to the extra-credit features.

Click here to read about the input file format.

Required Features

Note that for most of the required features, the images were rendered without any extra effects such as antialiasing, so as to place emphasis on the feature itself.

Assignment 3 Example (Phong Shading)

As a starting point, here is a rendering which is much like images generated in Assignment 3.

$ cat ex1.xobj
sphere 0 0 -10 5
mat    1   0.2 0.2 0.2    0.18 0.47 0.56    0.5 0.5 0.5   0 0 0   255 
$ time ../../raytracer ex1.xobj --headless --width 700 --height 700 --pl -5,8,-1,1,1,1 --outfile ex1.png
real  0m1.254s
user  0m1.170s
sys   0m0.080s

Arbitrarily Oriented Ellipsoids

This example comes courtesy of the Ray tracer implementation journal. Note that for this particular image, the reflections are turned off (they will be showcased later).

$ cat ex4-headless.xobj
# unit spheres
sphere 0 0 0 1
sphere 0 0 0 1
sphere 0 0 0 1
sphere 0 0 0 1
sphere 0 0 0 1

# transform into ellipsoids
scale      1   4 2 2
translate  1   0 0 -17

scale          2   0.5 1.5 1.0
ypr_rotate_deg 2   0   -45 -45
translate      2   -2    4 -17

scale          3   0.5 1.5 1.0
ypr_rotate_deg 3   0   -45  45
translate      3   -2   -4 -17

scale          4   0.5 1.5 1.0
ypr_rotate_deg 4   0    45 -135
translate      4    2    4 -17

scale          5   0.5 1.5 1.0
ypr_rotate_deg 5   0    45  135
translate      5    2   -4 -17

#     i   ka            kd      ks      kr      sp
mat   1   0.1 0.1 0.1   1 0 0   1 1 1   0 0 0   50
mat   2   0.1 0.1 0.1   0 1 0   1 1 1   0 0 0   50
mat   3   0.1 0.1 0.1   0 0 1   1 1 1   0 0 0   50
mat   4   0.1 0.1 0.1   1 1 0   1 1 1   0 0 0   50
mat   5   0.1 0.1 0.1   0 1 1   1 1 1   0 0 0   50
$ time ../../raytracer --headless --viewplane -1,-1,-3,-1,1,-3,1,1,-3,1,-1,-3 --width 1000 --height 1000 \
> --dl 0.57735027,-0.57735027,-0.57735027,1,1,1 --dl -0.57735027,0.57735027,0.57735027,1,1,1 \
> --outfile ex4-headless.png --depth 1 ex4-headless.xobj
real  0m5.196s
user  0m5.123s
sys   0m0.067s

Render polygons (triangles)

Here is a simple scene rendering 3 triangles with shadows

.
$ cat ex18.xobj
triangle  5 5 -17   1  4 -20   6 -1 -20
triangle -5 5 -17  -6 -1 -20  -1  4 -20
triangle  0 0 -15  -3 -4 -14   3 -4 -14

zrect   -50 -50 -23   -50 50 -23    50 50 -23   50 -50 -23

mat   1   0.1 0.1 0.1   0.88 0.36 0.2   1 1 1   1 1 1   50
mat   2   0.1 0.1 0.1   0.88 0.91 0.5   1 1 1   1 1 1   50
mat   3   0.1 0.1 0.1   0.88 0.91 0.5   1 1 1   1 1 1   50
mat   4   0.3 0.4 0.5   0.20 0.20 0.2   0 0 0   0 0 0    0
$ time ../../raytracer --headless --width 700 --height 700 --viewplane -1,-1,-3,-1,1,-3,1,1,-3,1,-1,-3 \
> --pl 3,3,5,1,1,1 --outfile ex18.png --depth 5 ex18.xobj
real  0m3.517s
user  0m3.463s
sys   0m0.053s

Shadows

This example also comes courtesy of the Ray tracer implementation journal.

$ cat ex2-headless.xobj
sphere   0  0 -20   3
sphere  -2  2 -15   1
sphere  -2 -2 -15   1
triangle 5  5 -17   1 4 -20   6 -1 -20

#     i   ka            kd      ks      kr      sp
mat   1   0.1 0.1 0.1   1 0 1   1 1 1   0 0 0   50
mat   2   0.1 0.1 0.1   1 1 0   1 1 1   0 0 0   50
mat   3   0.1 0.1 0.1   0 1 1   1 1 1   0 0 0   50

mat   4   0.1 0.1 0.1   0.1 0.1 0.1   1 1 1   0 0 0   50
$ time ../../raytracer --headless --viewplane -1,-1,-3,-1,1,-3,1,1,-3,1,-1,-3 --width 700 --height 700 \
> --dl 0.57735027,-0.57735027,-0.57735027,1,1,1 --dl 0.57735027,0.57735027,-0.57735027,0,0,1 \
> --outfile ex2-large.png --depth 5 ex2-headless.xobj
real  0m1.957s
user  0m1.897s
sys   0m0.057s

Reflections

Once again, this example also comes courtesy of the Ray tracer implementation journal. Last one though, I promise.

$ cat ex3.xobj
sphere  0  0 -17  2
sphere  0  4 -17  1.5
sphere  0 -4 -17  1.5
sphere  4  0 -17  1.5
sphere -4  0 -17  1.5

#     i   ka            kd      ks      kr            sp
mat   1   0.1 0.1 0.1   1 0 0   1 1 1   0.9 0.9 0.9   50
mat   2   0.1 0.1 0.1   0 1 0   1 1 1   0.9 0.9 0.9   50
mat   3   0.1 0.1 0.1   0 0 1   1 1 1   0.9 0.9 0.9   50
mat   4   0.1 0.1 0.1   1 1 0   1 1 1   0.9 0.9 0.9   50
mat   5   0.1 0.1 0.1   0 1 1   1 1 1   0.9 0.9 0.9   50
$ time ../../raytracer --headless --viewplane -1,-1,-3,-1,1,-3,1,1,-3,1,-1,-3 --width 1000 --height 1000 \
> --dl 0.57735027,-0.57735027,-0.57735027,1,1,1 --dl -0.57735027,0.57735027,0.57735027,1,1,1 \
> --outfile ex3.png --depth 5 ex3.xobj
real  0m3.900s
user  0m3.836s
sys   0m0.060s

Apply linear transforms to objects

Here is an example of a image where transformations are applied to both primitives, and groups of primitives. Note that the only primitives used to render this image were a x-y plane aligned rectangle, sphere, and axis aligned box. This image demonstrates use of scales, translations, and rotations.

$ cat ex20.xobj
zrect   -5 -5 0   -5 5 0  5 5 0   5 -5 0
euler_rotate_deg  1   -90 0 0

zrect   -5 -5 0   -5 10 0  5 10 0   5 -5 0
translate   2   0 0 -5 

zrect   -5 -5 0   -5 10 0  5 10 0   5 -5 0
euler_rotate_deg  3   0 -90 0
translate   3   5 0 0

box     2 0 -3  2.25 3 -1.5

sphere  0.25 0.75 -3  0.75

sphere  0 0 0  0.5 
scale     6   1 0.5 1
euler_rotate_deg  6  0 0 -45
translate   6   0.7 0.7 -1.4

sphere  0 0 0  0.5
scale     7   0.5 1 1
euler_rotate_deg  7   0 0 -45
translate   7   0.7 0.7 -1.4

euler_rotate_deg  all   0 30 0
translate   all   0 -1 0

mat 1   0.1 0.1 0.1  0.75 0.73 0.65   0 0 0   0 0 0   0
mat 2   0.1 0.1 0.1  0.35 0.18 0.00   0 0 0   0 0 0   0
mat 3   0.1 0.1 0.1  0.69 0.67 0.47   0 0 0   0 0 0   0

mat 4   0.1 0.1 0.1  0.62 0.46 0.28   1 1 1   0.3 0.3 0.3   50
mat 5   0.1 0.1 0.1  0.40 0.54 0.57   1 1 1   0.1 0.1 0.1   50

mat 6   0.1 0.1 0.1  0.34 0.45 0.18   1 1 1   0.03 0.03 0.03   50
mat 7   0.1 0.1 0.1  0.86 0.33 0.03   1 1 1   0.03 0.03 0.03   50
$ time ../../raytracer --headless --width 700 --height 700 --pl -1,5,1,1,1,1 --outfile ex20.png --depth 5 ex20.xobj
real  0m4.810s
user  0m4.713s
sys   0m0.023s

Point and Direction Lights

The previous examples were rendered with a mix of point and direction lights. Point lights are specified with a --pl flag, and direction lights are specified with a --dl flag to the program.

Write output to PNG file

All the previous examples were run with the --headless flag, which renders the image and saves the image in PNG format to the file specified by the --outfile flag. By omitting the --headless flag, the image is drawn to the screen using OpenGL/GLUT.

AABB Acceleration

AABB acceleration is controlled by the --accel flag. The implementation builds a bounding box hierarchy by using a sorting heuristic based on the center of the AABB described in both Shirley and here. To demonstrate the speed improvement, I first rendered the following grid of spheres without AABB acceleration (note that a print out of the input file for the sphere grid is omitted because it is long and not interesting).

$ time ../../raytracer --headless --viewplane -1,-1,-3,-1,1,-3,1,1,-3,1,-1,-3 --width 1000 --height 1000 \
> --dl 0.57735027,-0.57735027,-0.57735027,1,1,1 --dl -0.57735027,0.57735027,0.57735027,1,1,1 \
> --outfile ex5.png --depth 1 ex5.xobj
real  0m9.652s
user  0m9.579s
sys   0m0.043s

Now I apply the AABB acceleration and we can see speed improvements.

$ time ../../raytracer --accel --headless --viewplane -1,-1,-3,-1,1,-3,1,1,-3,1,-1,-3 --width 1000 --height 1000 \
> --dl 0.57735027,-0.57735027,-0.57735027,1,1,1 --dl -0.57735027,0.57735027,0.57735027,1,1,1 \
> --outfile ex5.png --depth 1 ex5.xobj
real  0m3.343s
user  0m3.276s
sys   0m0.030s

Refraction

I implemented both the Schlick approximation to weight the reflection and refraction contributions, and Beer's Law to attenuate the light travelling within the medium. Air was assumed to have an index of refraction of 1.0. This example is based on Robert Carroll's post on the newsgroup. Once again the input file is omitted because it is very lengthy. The key points however are that the grid is at z = -20, and the sphere is a dielectric material with n = 0.98 and Beer's Law constants 0.2, 0.2, and 0.2. This particular image below has a few aliasing artifacts which will be fixed below (in the extra credit section)!

$ time ../../raytracer --headless --depth 5 --accel --viewplane -1,-1,-3,-1,1,-3,1,1,-3,1,-1,-3 \
> --width 700 --height 700 --pl -5,4,2,1,1,1 --outfile ex14-headless.png ex14.xobj
real  0m7.565s
user  0m7.530s
sys   0m0.023s

Here is another example of refractions, using a texture mapped image for a background. The background was supplied by Graphics Hunt. This example makes use of antialiasing to get a really smooth appearance.

$ cat ex16.xobj 
zrect   -6 -6 -10  -6 6 -10    6 6 -10  6 -6 -10 

sphere  0 0 -6 0.75 

sphere  -0.5 -0.5 -4 0.25 
sphere  -0.5 0.5 -4 0.25 

box -0.5 -1 -0.15  0.5 1 0.15 

euler_rotate_deg 5 0 -30 0
translate 5 1 0 -7

texmat   1   1 1 1   0 0 0   0 0 0   0 0 0   0  mountain.png

dielectric_mat  2   0 0 0   0 0 0   1 1 1   50  1.5  0.1 0.1 0.1

dielectric_mat  3   0 0 0   0 0 0   0 0 0   0  .97  0.1 0.1 0.1
dielectric_mat  4   0 0 0   0 0 0   0 0 0   0  .97  0.1 0.1 0.1

dielectric_mat  5   0 0 0   0 0 0   1 1 1   50  1.33  0.1 0.1 0.1
$ ../../raytracer --headless --antialiasing 5 --depth 5 --accel --viewplane -1,-1,-3,-1,1,-3,1,1,-3,1,-1,-3 \
> --width 700 --height 700 --dl 0.57735027,-0.57735027,-0.57735027,1,1,1 --outfile ex16.png ex16.xobj
real  1m18.642s
user  1m18.418s
sys   0m0.040s

Extra Credit Features

Cone Primitive

An acute cone primitive was implemented based on this intersection algorithm. The parameters for a cone are the tip vertex, an angle theta for displacement (theta must be less than 90 degrees), and a max point. The direction vector for the cone is generated by taking max point - tip vertex. In the rendering below, both the floor and walls are not texture maps, but are instead images from here and here respectively.

$ cat ./ex21.xobj 
cone  0 0 0  0 2.3 0  0.34907

euler_rotate_deg 1 25 0 25 
translate 1  2.2 0.3 -2.5

box -1 0 -2  1 0.3 0.5
euler_rotate_deg 2  0 -45 0
translate 2  1.5 0 -2

zrect   -5 -5 0   -5 5 0  5 5 0   5 -5 0
euler_rotate_deg 3 -90 0 0

zrect   -6 0 0   -6 8 0  6 8 0   6 0 0
translate   4   0 0 -5 

zrect   -6 0 0   -6 8 0  6 8 0   6 0 0
euler_rotate_deg  5   0 -90 0
translate   5   5 0 0

sphere 1 0.25 -4  0.25
sphere 0.2 0.25 -4  0.25
sphere -0.6 0.25 -4  0.25

euler_rotate_deg  all   0 30 0
translate   all   0 -1 0

dielectric_mat 1  0.15 0.15 0.15  0.15 0.15 0.15  1 1 1  50  1.33  0.4 0.4 0.4
dielectric_mat 2  0 0 0  0.50 0.28 0.13  1 1 1  50 1.33 0.2 0.2 0.2

texmat 3   0.1 0.1 0.1  0.8 0.8 0.8   1 1 1   0 0 0  50  marble.png
texmat 4   0.1 0.1 0.1  0.8 0.8 0.8   1 1 1   0 0 0  50  tile.png
texmat 5   0.1 0.1 0.1  0.8 0.8 0.8   1 1 1   0 0 0  50  tile.png

mat 6  0.1 0.1 0.1  0.16 0.47 0.31  1 1 1  0.1 0.1 0.1  50
mat 7  0.1 0.1 0.1  0.16 0.47 0.31  1 1 1  0.1 0.1 0.1  50
mat 8  0.1 0.1 0.1  0.16 0.47 0.31  1 1 1  0.1 0.1 0.1  50
$ time ../../raytracer --headless --antialiasing 15 --width 700 --height 700 --al -1,4,1,0,1,0,0,0,1,1,1,1 \
> --outfile ex21.png --depth 5 ex21.xobj
real  36m42.478s
user  0m2.413s
sys   36m34.317s

Antialiasing

I implemented the supersampling technique of antialiasing described in both the lectures and Shirley. My particular implementation uses the jittered grid approach. First, let us fix the checkered image shown for refraction, because no antialiasing really does not do it justice. In all the examples I will show below which use antialiasing, I divide each pixel into a 5 by 5 grid to oversample.

$ time ../../raytracer --headless --antialiasing 5 --depth 5 --accel --viewplane -1,-1,-3,-1,1,-3,1,1,-3,1,-1,-3 \
> --width 700 --height 700 --pl -5,4,2,1,1,1 --outfile ex14.png ex14.xobj
real  2m53.496s
user  2m53.179s
sys   0m0.040s

Essentially every image I show from here on will have antialiasing turned on, since it looks a lot better. So we will definitely see more examples!

Area Lights

I use the implementation described in the book, where a light is modelled as some area, and a light vector is picked at random from the area of the light source. Below is an image I rendered above, but with area lights instead. For my program, area lights are specified by the --al parameter.

$ time ../../raytracer --headless --antialiasing 20 --width 700 --height 700 --al -1,5,1,0,2,0,2,0,0,1,1,1 \
> --outfile ex20-arealights.png --depth 5 ex20.xobj
real  29m43.810s
user  29m43.103s
sys   0m0.023s

Texture Mapping

I implemented texture maps for spheres and rectangles. In this particular example, the Miller cylindrical maps of the world are courtesy of Wikimedia Commons.

$ cat ex13.xobj 
sphere 0 0 0 4.5 
sphere 0 0 0 1 

texmat   1   0.1 0.1 0.1   1 1 1   1 1 1   0.1 0.1 0.1   255   miller-projection.png
texmat   2   0.1 0.1 0.1   1 1 1   1 1 1   0.1 0.1 0.1   255   miller-projection.png

euler_rotate_deg 1 -100 180 10
translate 1 -1.6 0.3 -20

euler_rotate_deg 2 -100 0 -10
translate 2 2 -2 -10
$ time ../../raytracer --headless --viewplane -1,-1,-3,-1,1,-3,1,1,-3,1,-1,-3 --width 700 --height 700 \
> --dl 0.57735027,-0.57735027,-0.57735027,1,1,1 --dl 0.57735027,0.57735027,-0.57735027,1,1,1 \
> --outfile ex13.png ex13.xobj
real  0m1.133s
user  0m1.083s
sys   0m0.033s

Rendering OBJ Meshes

I rendered a few of the OBJ mesh files provided by the Resources section. First is the Angel mesh.

$ time ../../raytracer --headless --antialiasing 5 --accel --viewplane -1,-1,-3,-1,1,-3,1,1,-3,1,-1,-3 --width 700 --height 700 \
> --dl 0.57735027,-0.57735027,-0.57735027,1,1,1 --dl -0.57735027,0.57735027,0.57735027,1,1,1 --outfile ex7.png --depth 1 ex7.xobj
real  3m55.506s
user  0m30.955s
sys   3m22.257s

Next is the Dragon mesh, rendered as a dielectric material (n = 1.5) against a sunset background courtesy of user chamel on Flickr.

$ time ../../raytracer --headless --depth 5 --antialiasing 5 --accel --viewplane -1,-1,-3,-1,1,-3,1,1,-3,1,-1,-3 \
> --width 700 --height 700 --dl 0.57735027,-0.57735027,-0.57735027,1,1,1 --outfile ex15.png ex15.xobj
real  22m47.453s
user  0m34.314s
sys   21m52.824s

Depth of Field

For DOF, I used the implementation strategy outlined here. The parameters that the user can set are the size of the lens (assumed to be a square), and the focal plane. Background image (which is blurred out) is courtesy of the Kurt Martin Event Team. We start with a simple scene rendered without DOF.

$ cat ex17.xobj
sphere 0 0 -5 1 
sphere 3 3 -8 2 
sphere -6 -6 -15 4 

zrect -25 -25 -20 -25 25 -20 25 25 -20 25 -25 -20
texmat 4  1 1 1   0 0 0   0 0 0   0 0 0   0   grassfield.png 
mat   1   0.1 0.1 0.1   0.2 0.35 0.15   1 1 1   0.1 0.1 0.1   50
mat   2   0.1 0.1 0.1   0.52 0.29 0.09   1 1 1   0.1 0.1 0.1   50
mat   3   0.1 0.1 0.1   0.28 0.46 0.46   1 1 1   0.3 0.3 0.3   50
$ time ../../raytracer --headless --antialiasing 5 --pl -5,5,1,1,1,1 --outfile ex17-nodof.png ex17.xobj
real  0m12.287s
user  0m12.246s
sys   0m0.027s

Now we will make the nearest sphere (the green one) in focus, and everything else out of focus. DOF is specified as a command line argument to the program, --dof. The parameters are focal plane (absolute z position), lens side, and how many samples to take for each lens.

$ time ../../raytracer --headless --dof -5,1,5 --pl -5,5,1,1,1,1 --outfile ex17-dofnear.png ex17.xobj
real  4m42.735s
user  1m49.626s
sys   2m50.662s

And now we will make the farthest sphere (the blue one) in focus, and everything else out of focus.

$ time ../../raytracer --headless --dof -15,1,5 --pl -5,5,1,1,1,1 --outfile ex17-doffar.png ex17.xobj
real  5m2.038s
user  2m18.444s
sys   2m39.953s

Input File Format

The file format used for the raytracer (xobj extension) is an ad-hoc format made for this project. Its main goal was to be easy to parse, since I did not want to have to write a lexer/parser with Flex/Bison. It is also meant to be a superset of the OBJ file format, so I could take the provided meshes and render them with ease. The commands are described below

Return home