CS 6620.001 "Ray Tracing for Graphics" Fall 2014
Welcome to my ray tracing site for the course CS 6620.001, being taught at the University of Utah in Fall 2014 by Cem Yuksel.
Welcome fellow students! I have lots of experience tracing rays, and with graphics in general, and so I'll be pleased to help by giving constructive tips throughout (and I'll also try very hard to get the correct image, or say why particular images are correct as opposed to others). If you shoot me an email with a link to your project, I'm pretty good at guessing what the issues in raytracers are from looking at wrong images.
Hardware specifications, see bottom of page.
Timing information will look like "(#t #s ##:##:##)" and corresponds to the number of threads used, the number of samples (per pixel, per light, possibly explained in context), and the timing information rounded to the nearest second.
Project 14 - "Teapot Rendering Competition"
First, my final results.
- "Glow" (Full HD; click to view full resolution) (16t 512,^0s 16:53:39):
- "Splintered Caustics" (Full HD; click to view full resolution) (way too long):
- "Destructive Interaction" (way too long):
- Bonus Unofficial Entry: "Darth Teapot" (half size; click to view full resolution) (16t 10,^0s 00:02:58):
(For the slideshow, use the arrow keys to navigate and escape to exit.)
Backstory: "Destructive Interaction"
For many things, Blender is great—by which I mean it's a powerful tool in capable hands.
Unfortunately, mine are not capable.
To render the teapot shatter scene, I had to get a teapot. I used the standard one from class, but, since my renderer prefers glass to be volumetric (because, um, that's what it is in real life), I needed a teapot with actual thickness. I couldn't find one online that worked, so instead I painstakingly created a solid object that's not topologically dumb (and doesn't have source URLs baked into the surface as geometry defects (like one example I saw)).
From there, I used Blender's massively crufty but still highly winning "Cell Fracture" tool to shatter my beautiful teapot into \(5000\) shards. This took a very long time (most people max out the settings at \(250\)). I basically followed the tutorials (of which there are several; this video was the most helpful, but I sourced some tips from this series, whose user seemed to be more adept).
There are various things I hate about Blender. Like many other modeling tools, it adopts the perhaps wrong \(\vec{z}\)-up convention. Also, it adopts the definitely wrong gravity \(9.8\frac{m}{s^2}\).
I could live with these failures if they were actually consistent. Turns out, they're not. In Blender, you cannot as far as I can tell vary the Physics \(\Delta t\). This makes it, for example, very difficult to render things in slow-motion. You can kindof distort time during the render. And you can scale everything up so as to make the forces look like they're acting more slowly, but these are broken hacks (that didn't work anyway).
You can't disable gravity either. You can switch to "Game Mode" and set the gravity to zero, but this actually doesn't do anything. The accepted solution seems to be to add a force to counteract gravity exactly, so that gravity has no effect. This is not a solution. This is a horrific workaround to a broken system.
Therefore, after shattering the teapot, I exported it as a ".obj" to my own physics system (also based on Bullet Physics—except, since it's not wrapped in a feature-deficient GUI, I can set important things, like gravity, to be correct (in this case, gravity is \(-9.80665 \frac{m}{s^2}\) and the timestep is \(0.005\frac{s}{frame}\))).
How did I export? Slowly and painfully. The Blender .obj exporter works absolutely terribly for this, evidently. Blender started crashing randomly during the save (writing only 600-some shards). I disabled the triangulate faces option (which seemed to improve things). However, my loader doesn't support non-triangular faces (because that would mean doing something ill-defined). So, I decided to do the triangulation in Blender.
The triangulate method worked very nicely (to do this, I had to join everything into one object (fast) and then split it again by connectedness (several hours)). I then tried to export it, and Blender started crashing again. I was eventually able to export parts of the object at a time, but Blender couldn't get the back part of the teapot for some reason. Since Blender's crashes terminate immediately, I had to run it from a shell to be able to see the output (I didn't even know it had output, since it doesn't display the terminal unless you tell it to).
The output told me to look in (on Windows) "C:/Users/IANMAL~1/AppData/Local/Temp/shatter2.crash.txt". Apart from a few lines of stuff (e.g. such-and-such a file. etc.), the only error within the file was this:
bpy.data.window_managers["WinMan"].(null) = False # Property
I know Python, so maybe I could have fixed the script. But no. No backtrace.
After futzing around some more, I gave up and downloaded 3ds Max. Of course, "download" isn't the right word. You actually download a download manager with downloads an installer which downloads 3ds Max. Each stage of the chain is broken, bloated, and stupid. It failed several times before demanding I restart my computer before I'd even started installing. Four IDEs, a web browser, miscellaneous files and configured search windows, and a couple of uninterruptible processes running in the background? Oh sure, I'd love to restart my computer just for you. It's so considerate that you're making sure that the registry-entries-you're-writing-without-my-permission-to-install-rootkits-to-ensure-I-really-am-a-grad-student aren't conflicting with Windows Update. I'm almost morally obligated to sign over all my rights to you since even though this is my computer, it's still your application I'm running on it!
Of course, after rebooting, the connection to the internet had been interrupted—which caused the install to crash yet again. So far, so good. Just the quality I'd expect for a \(\$3,\!675\) piece of software (or, through our subscription plan, only \(\$185\) per month!). At least it's "free*" for students.
So I started the download again. Then I did something stupid. I closed the browser window (since it's 2014, and only idiots who never outgrew AOL do not understand downloading in the background). Except that Autodesk makes no kind of sense. Closing the browser tab not only aborted the download, it crashed the download manager. On investigation, I kid you not: there are scripts running on that page that actively kill the download when you close the window.After pkill
ing all the hung processes, I sat down and thought this through. Eventually, I derived a cryptographic proof—a real, honest-to-Zeus mathematical proof—that killing downloads is literally never necessary. You open a connection, two-way hashed identity verification, set up an encrypted tunnel. Blah blah.
But, the simian stand-in for a coder that evidently wrote this cruft without getting fired will likely never know the extent of my fury.
There was nothing to do but start the download again. This time, it crashed for literally no reason. I was about to start it yet again when I realized that somehow, it had put a start icon on my desktop. I figured maybe at least the application had installed, and so I tried it. Things then started happening very quickly. In about \(30\) seconds, the program started up, told me it was going to monitor application usage, told me it was checking my license and sent an email confirming my student license. Then the start icon disappeared and the program claimed it had encountered a problem. I figured some temporaries hadn't been deleted, so I set about deleting them myself and reinstalling. And then my computer shut itself down.
I realized what was happening, and by sheer luck terminated an admin process that would have massively corrupted the filesystem just in time (seriously—two seconds later, and I wouldn't have been able to boot) (I lost several paragraphs of this text, though—about a page has been reconstructed from memory).
This is the programmer equivalent of assault. A computer is your mental extension. It is the most powerful tool you own, and when you put applications on it, it's a matter of trust. When your computer does something unexpected because of what someone else did, it hurts. And spontaneous, forced reboots are the strongest such gesture. I don't know what happened, but I have my suspicions.
Remember, at this point, the problem is still writing a bunch of triangles to a file. I'm (failing) to download a software package that costs as much as a used car just so that, somewhere within its \(6.72\)GB hulk, a subroutine can call "fopen" and write \(100\)MB of ASCII text. Four hours wasted, at this point.
I finally got 3ds Max started. Then, it crashed on loading Blender's Collada export. So, I tried exporting ".fbx" from Blender (this crashed Blender). Blender can export ".x3d", but 3ds Max doesn't support it. My first success came from ".stl", but then each object is welded to its neighbors. I figured I'd have another go at Blender, but then I realized that some of the shards were split into non-manifold pieces. So all the data was worthless anyway.
After a few successful tests with fewer breaks and different settings (such as, in particular triangulating before shattering), I ran the shatter simulation again, which took another three hours. This time, the ".obj" export worked perfectly and I was able to quickly load it into my library. After some related tweaking, I was finally able to get a simulation I liked.
To render the simulation, I needed to export the objects from my simulator, calculating different transforms for the start and end of each frame for motion blur. In addition, it had to export a scenefile for each frame, which was a refreshing bit of coding to implement. Getting my renderer to render frames based on calls from a Python script was also fairly simple.
I tweaked the shatter simulation so as to simulate a supersonic shockwave from the bullet (which is simulated traveling at \(1250\frac{m}{s}\)). The effect was originally just a simple lateral force added behind the bullet, which produces this:

This looks lots better, but unfortunately, it causes a weird acceleration behind the bullet:
This can be rectified by adding a falloff with reciprocal distance, producing the much more satisfying:

Visualizing the shockwave is fairly simple: just add a hollow, thin-shell refractive cone (I couldn't find any refractive index data, so I took a guess at \(2.0\)):

Time to render the first video that actually has a chance of being good. For this, I set up my script to have multiple parallel renders running in parallel. Also, the frames were being generated at the same time (I eventually terminated the simulation at \(501\) frames):


I tweaked the refractive index on the shockwave (down to \(1.6\)) and tweaked the resolution to make it actual HD. I also bumped the sample count from \(4\) to \(64\). These changes had to be done in the renderer itself with nasty hacks, since the frames were already generated. The sample count and resolution changes drastically increase rendering time. The previous videos took maybe \(23\) seconds per frame (half for loading). The first frame of the final version, by contrast, took \(9\) seconds to load and \(35\) minutes, \(7\) seconds to render! At this rate, rendering the other \(500\) frames would take \(12\) days, \(4\) hours, and \(38\frac{1}{3}\) minutes!
To help, I enlisted my lab computer which renders (the admittedly much easier) frame \(200\) in \(17\) minutes \(54\) seconds. I also tried setting up my tracer on other computers. A render server for HWRT couldn't be used because the OS is too outdated and I can't compile besides. I couldn't use my undergrad's CS account computer for the same reason.
To get the final result, I composited all the frames in After Effects and added some screen-space effects, including time warp. I calculated that the final render took around \(72\) CPU-core-days to render (with parallelism, total \(24\) logical cores on two computers, this worked out to be several days).
Backstory: "Glow"
For the teapot glow scene, I quickly implemented a hackish volume integrator. The first okay result (16t 16,^0s 00:02:40):

That one uses \(\sigma_a:=0.1,L_e:=0.4<0.1,0.5,1.0>\). There is no scattering, but if there were it would be the Henyey–Greenstein phase function with \(g:=0.6\). It looks so thick because of the emission. It still looks turbid even when there is no absorption at all. In any case, again, there's no scattering either! Unlike most of the rest of my efforts, the math behind it is also unapologetically wrong.
After a minor improvement to the math and a touch of depth of field (16t 4,^0s 00:00:46):

I wanted the depth of field to be just perceptible, and based on some earlier test I wanted a brighter teapot. After a few more trials, I got (16t 4,^0s 00:00:47):

After tweaking the resolution to full HD, closing the field of view a bit, adjusting the brightness to compensate, and adding \(10^\circ\) rotation (16t 16,^0s 00:03:51):

After careful revision, I improved the math some more and added some absorption (16t 16,^0s 00:04:38):

I set up a version that is larger, has more samples, and was rendered on my lab machine, which I overclocked to 4.4GHz (click for full HD) (16t 512,^0s 04:09:06):

I had intended for this to be the final version, but I decided the floor could use a checkerboard to help give context. Here's my test (16t 16,^0s 00:08:37):

Backstory: "Splintered Caustics"
This one was essentially an accident. I was picturing a blue teapot with some ocean-wave-like caustics on a simple floor, and just wanted to see what it would look like. Since I had other renders, I figured it might be a nice addition. But, the first result was so amazing that I felt compelled to throw more compute at it.
The light emits \(100,\!000\) photons, leading to about a quarter billion caustic photons being stored. Heavily compressed, this is still almost \(650\)MB! I had a hard time fitting this into memory—and, in retrospect, it probably didn't.
I set up the final render around \(02\!:\!00\), thinking it would be done in the morning. When I woke up, it was only \(39\%\) done. I figured it would be the end of the day. But, the glass rendered absurdly slowly. I rendered the top part of the scene on my lab computer. Even so, the entire render took days. This was probably due to accessing the photonmap from disk multiple times for every glass pixel.
In the end, there were a few incomplete scanlines that didn't get as many samples as they should have—but I called it done anyway.
The final version is the original render with swapped red and blue channels. Here are all possible permutations (click to view full HD) (top left was original):
![]() | ![]() |
![]() | ![]() |
![]() | ![]() |
Late the day before the competition (around \(03\!:\!00\)), I decided to try adding volume caustics, which I had been meaning to add but didn't. After some work, I got the caustics buffer, which handles single scattering (fourish hours render time):

This allows for the final image:

Proceed to the Previous Project or Next Project.
Hardware
Except as mentioned, renders are done on my laptop, which has:
- Intel i7-990X (12M Cache, [3.4{6|7},3.73 GHz], 6 cores, 12 threads)
- 12 GB RAM (DDR3 1333MHz Triple Channel)
- NVIDIA GeForce GTX 580M
- 750GB HDD (7200RPM, Serial-ATA II 300, 16MB Cache)
- Windows 7 x86-64 Professional (although all code compiles/runs on Linux)