Click here to jump straight to the extra-credit features.
Click here to read about the input file format.
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.
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
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
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
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
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
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
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.
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.
$ 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
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
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
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!
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
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
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
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
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
v x1 x2 x3where x1, x2, and x3 are doubles describing the x,y,z coordinates of the vertex.
f v1 v2 v3where v1, v2, and v3 are indices of the 3 vertices of the triangle.
sphere c1 c2 c3 rwhere c1, c2, and c3 describe the x,y,z coordinates of the center, and r is the radius.
triangle v11 v12 v13 v21 v22 v23 v31 v32 v33where v1 = (v11, v12, v13), v2 = (v21, v22, v23), and v3 = (v31, v32, v33).
box l1 l2 l3 r1 r2 r3where min = (l1, l2, l3) and max = (r1, r2, r3).
zrect ll1 ll2 ll3 ul1 ul2 ul3 ur1 ur2 ur3 lr1 lr2 lr3where LL = (ll1, ll2, ll3), UL = (ul1, ul2, ul3), UR = (ur1, ur2, ur3), and LR = (lr1, lr2, lr3)
cone v1 v2 v3 x1 x2 x3 thetawhere the tip vertex is (v1, v2, v3), the direction vector is generated by (x1, x2, x3) - (v1, v2, v3), and theta (less than 90 degrees) gives the angle.
translate idx tx ty tzwhere idx is the geometry index to apply to (can be "all" for every geometry), tx is the x translation, ty is the y translation, and tz is the z translation.
euler_rotate idx alpha beta gamma euler_rotate_deg idx alpha_deg beta_deg gamma_deg ypr_rotate idx yaw pitch roll ypr_rotate_deg idx yaw_deg pitch_deg roll_degwhere idx is the geometry index to apply to (can be "all" for every geometry), alpha, beta, gamma are the Euler angles to rotate, and yaw, pitch, roll are the yaw, pitch, and roll angles to rotate by.
scale idx sx sy szwhere idx is the geometry index to apply to (can be "all" for every geometry), sx, sy, and sz are the scale factors to apply to the x, y, and z direction respectively.
mat idx ka_r ka_g ka_b kd_r kd_g kd_b ks_r ks_g ks_b kr_r kr_g kr_b spwhere idx is the geometry index to apply to (can be "all" for every geometry), ka is the ambient coefficient, kd is the diffuse coefficient, ks is the spectral coefficient, kr is the reflective attenuation, and sp is the power term for specular shading.
texmat idx ka_r ka_g ka_b kd_r kd_g kd_b ks_r ks_g ks_b kr_r kr_g kr_b sp filewhere idx is the geometry index to apply to (can be "all" for every geometry), ka is the ambient coefficient, kd is the diffuse coefficient, ks is the spectral coefficient, kr is the reflective attenuation, sp is the power term for specular shading, and file is the PNG file to use as the texture map.
dielectric_mat idx ka_r ka_g ka_b kd_r kd_g kd_b ks_r ks_g ks_b sp n a_r a_g a_bwhere idx is the geometry index to apply to (can be "all" for every geometry), ka is the ambient coefficient, kd is the diffuse coefficient, ks is the spectral coefficient, sp is the power term for specular shading, n is the index of refraction, and a is the Beer's Law coefficients.