I’ve been dabbling in Common Lisp before but this time I wanted to learn a bit more about Scheme. To accomplish this I chose to start working on a naive ray tracer, a nice and simple project, using Racket Scheme. My goals were (are) to learn more about Scheme itself but also get more used to functional programming paradigms.
I’m pretty happy with the current state of the ray tracer, especially since it took a few evenings to get this far. I do have one case where I directly mutate the environment which I’m not proud of and am working on getting rid of, but other than that I’m happy with the structure. Having said that, as much as I like functional programming I don’t really use functional languages that often. You should probably not take this example as the best way of writing functional code and I would definitely not recommend learning Scheme from me.
The ray tracer is pretty basic at the moment but I intend to keep updating it. It only spawns light rays on geometry hits for the shading and has no shadows, multiple bounces or even area lights yet. It does several samples per pixel for as long as you leave it running, but it’s super slow. At some point I’ll switch to typed Scheme and look into optimizing but not just yet.
Most of the code will no doubt change with time, but here’s how things are set up at the moment. The scene is a list of primitive instances:
(define *scene* `(,*triangle* ,*sphere* ...))
Each primitive instance is a list containing everything it needs to be intersected and shaded:
(define *triangle* `(,intersect-triangle v0 v1 v2 normal color)) (define *sphere* `(,intersect-sphere position radius color))
The first element in both primitive types is the intersection function. We don’t really care how long the list is or what it contains because the scene traversal function contains this part:
(map (lambda (object) ((first object) ro rd object)) (get-scene-objects))
For each primitive in the scene we evaluate the first element in the list and pass in a ray and the whole primitive. We assume the primitive’s intersection function will know what do with the instance and return us a color.
This will do for now but it’s not very nice. Firstly, the intersection function needs to know the structure of the whole primitive. Secondly, there will be a very limited amount of primitive types. At the moment there’s only intersection functions for spheres and triangles, and it’s likely there won’t be need for much more. One might argue that keeping the intersection function in each primitive instance is a bit much, but something is needed to identify the primitive type. Whether this is a flag or the actual intersection function doesn’t matter at this point, but it will when you need to define lots of instances. It’s a non-issue right now which I chose to ignore. I will probably start looking a bit more at Racket’s class system and do something with that when the need for more functionality arises. Plus, I really like functions as first-class citizens, which leads me to my next point.
Each primitive has a color as the last element in the list. This won’t be enough when we want more than basic diffuse shading. What you could do instead is store a shading function that can again vary between primitive instances and do more than just return a color, e.g. spawn secondary rays, evaluate BRDFs, etc. This is stepping into material territory, a whole new world of fun, so I’ll defer this for later.
I’ll keep these posts short. You can see the current progress on Github if you’re interested. Next time I’ll try to justify the rest of the code. But it might change first!