Devlog: Week 8: Writing a Ray Tracer in Julia
It is real long since I studied coordinate geometry or computer graphics. This last week, dusting the very little remains of what I knew, I decided to work on a ray tracer. When I started, I did not know what a ray tracer was. Slowly, I understood that it was one algorithm that is used to construct a 3D scene on a 2D screen. Broadly, this is how it works
Define the eye/camera/viewpoint Define the screen in m*n pixels Shoot a ray for each pixel in the screen from the eye. For each ray, Find nearest intersected surface Find a color for the point based on surface reflection/refraction or the light source and shading model Plot the color on the pixel
I decided to build one that would trace spheres, so I had to define 3 major types - Vector, Ray, Sphere, and a helper type Intersection to store where an object intersected with the screen.
type Vector x::Real y::Real z::Real end type Ray origin::Vector direction::Vector end type Sphere center::Vector radius::Real color::Vector end type Intersection point::Vector distance::Real normal::Vector object end
Interestingly when I went about defining methods for the vector, I found Julia had inbuilt support for finding dot and cross products, so all I had to do was piggyback on those implementations and build my vector class. Also, thanks to Julia's multiple dispatch mechanism, overriding inbuilt operators was as simple as this:
function +(a::Vector, b::Vector) return toVec(toList(a) + toList(b)) end
Once I'd implemented it all, and fixed all bugs that produced seemingly awesome but wrong images, the main loop consisted of this:
for x in 1:imageWidth, y in 1:imageHeight traceWorker(x, y, imageArray, cameraPos, lightSource, objects) end //See full implementation <a href="https://github.com/madhuvishy/ray-tracer/blob/master/tracer.jl#L119">here</a>
☁ ray-tracer [master] ⚡ time julia tracer.jl julia tracer.jl 11.55s user 0.26s system 98% cpu 11.933 total
But having used Julia, I had to put it's parallel processing capabilities to test. And because each of the calculations in ray tracing are independent of each other, I could put this, to use. And now my loop became...
@parallel for x in 1:imageWidth, y in 1:imageHeight traceWorker(x, y, imageArray, cameraPos, lightSource, objects) end //See full implementation <a href="https://github.com/madhuvishy/ray-tracer/blob/master/tracer.jl#L119">here</a>
Sweet! Let's check the new running time.
☁ ray-tracer [master] time julia tracer.jl julia tracer.jl 6.44s user 0.11s system 99% cpu 6.566 total
Yay! Down by almost 50%. Pretty sure there are more ways to optimize my code, but this is how far I've gotten till now.
Now, for our final showdown.
Woah, but no...
Ummm, circles or spheres?
Thanks to fellow HackerSchooler Lita Cho for patiently explaining how ray tracers work :) Check it out at: https://github.com/madhuvishy/ray-tracer/