As a pet project I’m writing a ray tracer in Lisp. This is not because I particularly believe the world needs yet another amateur ray tracing program, but because I thought it would be a really good way to practice writing clean, object-oriented Lisp. My ray tracer doesn’t yet support all the fancy features that define a true ray tracer, but I’m slowly adding more capabilities whenever free time and motivation happen to coincide.
Ray tracing is a technique that produces incredibly realistic 3D graphics by simulating the physical behavior of light. By properly simulating light and optics a ray tracer can produce photo-realistic shadows and reflections. Unfortunately this simulation process is a little slow meaning that while it works great for high-quality art projects it isn’t usually a viable option for things like 3D movies or games.
Probably the most interesting thing about ray tracers is that they work backwards. In the real world rays of light shoot out of light sources (lightbulbs, fires, the sun) and then bounce around the world until a few of them hit your eye and let you see. You might think that a ray tracer would work the same way.
But there are big problems with that approach. Remember, only a small percentage of light ever reaches the viewer while the rest bounces into space or gets absorbed by the environment. So if a program calculates a path for every ray of light in the virtual world it’s going to be wasting a lot of time on light that the viewer doesn’t even see. It would technically work but the program would take so long to complete that it would be useless.
Ray tracers avoid this problem by flipping the situation around. Instead of starting at the light source they start with the viewer. For each pixel in the image the ray tracer calculates a path starting at the viewer and extending into 3D space. If that path hits an object the ray tracer then locates nearby light sources and calculate how much light they are shining on that object and at what angle. The program now has a complete path from light source to object to viewer. This is the same information it would eventually get if it started at the light source and worked forward, but by starting with the viewer we guarantee that we only find the paths that actually matter for the final image.