Ray Tracer in Python (Part 5) - Show Notes of "Some Light Reflections"

6 min read · Posted on: May 12, 2020 · Print this page

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:

Render of two balls
Render of the two balls scene

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.

Procedural Materials

A plainly colored object is not quite interesting so you would find even the earliest ray traced images containing a chessboard pattern:

The Compleat Angler
The Compleat Angler (1978) by Turner Whitted

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.

Plugin Scenes

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.

Accelerating Python

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:

  • Introduction
    • Laws of Reflection
    • Stack Overflow
    • Scene Definition
  • Sub-problem: Some Light Reflections
  • Coding the solution
    • Chessboard Material
    • Ground Plane
    • Config vs Code as Config
    • Speedup

Here is the video:

Code for part five is tagged on the Puray Github project

Show Notes

Books and articles that can help understand this part:

Further reading on ray tracing:

Note: References may contain affiliate links

Arun Ravindran profile pic

Arun Ravindran

Arun is the author of "Django Design Patterns and Best Practices". Works as a Product Manager at Google. Avid open source enthusiast. Keen on Python. Loves to help people learn technology. Find out more about Arun on the about page.

Don't miss any future posts!

Comments →

Next: ▶   Ray Tracer in Python (Part 6) - Show Notes of "Firing All Cores"

Prev: ◀   Ray Tracer in Python - Show Notes of "Ray Tracing a Coronavirus"

Up: ▲   Blog

Featured Posts

Frequent Tags

banner ad for Django book


powered by Disqus