You are reading a post from a multi-part series of articles
- Ray Tracer in Python (Part 1) - Show Notes of "Points in 3D Space"
- Ray Tracer in Python (Part 2) - Show Notes of "Revealing the True Colors"
- Ray Tracer in Python (Part 3) - Show Notes of "3D Balls in 2D Space"
- Ray Tracer in Python (Part 4) - Show Notes of "Let there be light"
- Ray Tracer in Python - Show Notes of "Ray Tracing a Coronavirus"
- Ray Tracer in Python (Part 5) - Show Notes of "Some Light Reflections"
- Ray Tracer in Python (Part 6) - Show Notes of "Firing All Cores"
One of the fascinating ways to build a complex system like a computer is to build it from scratch using simple logic gates (a good book which shows how is The Elements of Computing Systems). Same goes for late John Conway’s Game of Life which starts with a simple set of rules but shows very life-like behaviour. This is called Emergent complexity. I guess the only way to understand something that seems complex is to understand its basic principles.
Look carefully at the, by now familiar, image that we will be rendering by the end of this part:
Notice within the pinkish-purple ball there is a reflection of the red ball reflecting the pink ball. This kind of detailing that unfolds on closer inspection is what gives realistic renders its beauty. Yet it is governed by the most basic laws of Physics like the laws of reflection.
The law tells us that the angle of reflection is the same as the angle of incidence. But if we apply it to the world of vectors a rather different looking formula emerges that could be derived from the very same law. The reflected ray is again traced and the process continues.
If you have been following the series closely so far, then you might point out that we have already dealt with this earlier. Yes, diffuse and specular shading are special cases of a material reflecting light. But since we are now considering mirror-like reflecting surfaces it is time to look at the general formula for reflection.
The computation for each pixel will now increase many fold so the overall render time will increase proportional to the maximum depth of reflections we need.
A plainly colored object is not quite interesting so you would find even the earliest ray traced images containing a chessboard pattern:
So I introduce a chessboard pattern generated procedurally into the scene. Procedural textures are fascinating and a lot of fun to make. Compared to image textures, they have almost infinite detail. Sort of like analog versus digital.
The chessboard pattern’s formula is easy to guess so it is an ideal introduction. Once you start playing around there is an entire universe of textures to explore with marble, Voronoi and Perlin noise patterns. Some even go to the extend of building entire scenes with only procedural textures. This is deeply satisfying but probably pointless.
Most toy raytracers are happy generating their scene in the main program. But this quickly becomes frustrating when you want to render a couple of examples. The straightforward solution would be to define a scene as data say using JSON and import the scene given as an argument. This is how games like Doom load levels.
JSON, YAML and other configuration languages are deceptively simple to read but you could spend a lot of time writing them due to their tiny quirks with commas and whitespaces. It is also not suited for scenes generated procedurally which is happens quite a bit in ray tracers. You would soon wish if these languages were Turing complete. So I decided to ditch all that and use plain old Python instead.
To be honest, I was not comfortable in allowing a given Python file to describe a scene. But the power and flexibility it allows is really a great tradeoff for the security. I used importlib to import modules inspired by Django.
I can now define a new procedural texture material class inside a scene! This makes the ray tracer quite extensible like a plugin system. I love this approach and look forward to trying this in future projects.
Towards the end we see a dramatic 7X speedup of the ray tracer due to the use of PyPy. I also mention my rough rule of thumb to increase Python performance:
Processor Bound? Try Pypy.
IO Bound? Try AsyncIO.
If you are learning to improve the performance of your Python program, this would be pretty bad advice. In that case, make sure you first profile your program and identify the performance hotspots. Then try different ways to optimize those places. After you have tried all that and the performance is still bad, then you can use my rule of thumb for unconventional ways to get great results.
Ray tracing concepts
With this part, I would have covered all the basic ray tracing concepts that I had planned to cover. The next part would be about improving the performance of the ray tracer by using multiple cores.
Many have contacted me asking whether I would be covering topics like Dielectrics, Depth of Field, Anti-aliasing etc. I think there are enough books like Ray Tracing Gems and Ray Tracing in One Weekend which cover all that and much more. If you have followed this series then reading those books would be much easier and you would have a ready implementation to tinker with.
Nevertheless, I may work on a follow-up if people find that useful and time permits. So do let me know.
These are the topics we will cover in this episode:
- Laws of Reflection
- Stack Overflow
- Scene Definition
- Sub-problem: Some Light Reflections
- Coding the solution
- Chessboard Material
- Ground Plane
- Config vs Code as Config
Here is the video:
Code for part five is tagged on the Puray Github project
Books and articles that can help understand this part:
- Derive Vector Reflection Formula: Understand how the formula can be derived from the laws of reflection.
Further reading on ray tracing:
- Ray Tracing in One Weekend - The first in a series of minibooks. This is followed by two more minibooks Ray Tracing: the Next Week and Ray Tracing: The Rest Of Your Life. You may find free editions of these books online but I am not completely sure they are approved by the author.
- Ray Tracing Gems - An excellent update to the previous set of books explaining the latest techniques focusing on real-time ray tracing.
Note: References may contain affiliate links