Ray Tracing 001: Introduction


Introduction

TL;DR - The following pages are about ray tracing.

That is, about my learning journey. These pages will touch infos from various sources, some math and physics fundamentals, some programming details, as well as approaches to optimize performance, and everything else I learned on my way to ray tracing.

I will begin with Peter Shirley’s excellent book Ray Tracing in a Weekend and his following two volumes Ray Tracing - The Next Week and Ray Tracing - The Rest of Your Life.

I found the first book on Amazon Kindle some time ago, but it’s now available for free at raytracing.github.io.

But this will not be limited to his books alone. A few years ago I took an edX computer graphics course taught by Prof. Ravi Ramamoorthi that provided a great introduction to OpenGL and concluded with a ray tracer as well. He recently published a second course building on his first. I also plan to explore multi-threaded and parallel techniques with OpenMP, CUDA, OpenCL, as well as more advanced articles, tutorials and literature including Ray Tracing Gems.

Further down in my timeline I want to produce some animations as well: First, a ray-traced animation of the Triadic Ballet in time for the 100th anniversary of its premiere in September 2022, then explore the realm of physics-based animation and fluid simulations. But let’s start with a single simple image first. :)

I tested and built the code with the GNU compiler on Linux Mint and Windows+cygwin as well as Microsoft C++ with Visual Studio Code on Windows. It should work on other platforms as well, but please let me know if something does not work.

I also want to look into and learn more about cmake, jenkins and other tools used in the real world, but this will have to wait until it makes sense for this project. The first programs will be small and simple, and I want to offer interactive web-applets as well, so you can play and see what happens if certain parameter values are changed. At this time it makes more sense to forcus more on graphics and less on workflow tools.

In the last 20+ years I’ve mostly been involved with web development, database-, ecommerce- and marketing-support applications and the like. My programming adventures in graphics date back to my college days, and to be honest, I have hardly any working experience with computer graphics, animation, mathematics, physics, none of the fun stuff, nothing scientific or academic, flashy or artistic.

I have to admit that some topics have been and still are a little difficult to grasp. Sometimes I have to step back for a refresher or learning a prerequisite to fill a knowledge gap before I can resume my original project. I will include these excursions as well in case someone is in a similar situation and needs a refresher in C++ or math.

To bridge the gap between web and graphics programming, I will also include a Javascript version in my source code. I’d like to add some web-interactivity, but I’m also interested to see how it will all compare to a compiled language like C++. I’m sure the scenes will soon become too complex and time-consuming to run in a browser, especially without any optimizations, but let’s see how far I can take this and what I can do to improve performance.

Recently I’ve also added a WebAssembly version with Emscripten to my source code. I added just a few lines of code and compiler directives for Emscripten based on some SDL tutorials I found. I’m new to WebAssembly and Emscripten, and I’m sure there are better ways to do it. But the code compiles and produces web-friendly results that actually perform quite a bit better than pure Javascript. I’ll probably explore WebAssembly once I’ve completed the basics. If you find the first chapters laughably simple and buggy, I hope you will notice an improvement in the later chapters. :) That’s part of the learning experience.

The complete code is available on GitHub here: github.com/celeph/ray-tracing.

For the original source code (the correct “master solution” so to speak) of the first chapters, please visit Peter Shirley’s book repository. His original repository is very active and gets improved constantly. I highly encourage you to work through his book for the complete story.

My pages will spend more time on some aspects, less time on others, perhaps a slightly different perspective after having read other articles or books. I plan to summarize in my own words what a chapter is about and what I’ve learned. I may add some extras, drawings, applets, more verbose math, C++ and my own experiments not found in the original book, but I don’t want to just repeat the work of the original author(s).

They have done all the work already, and it would be a waste of time to just copy it all. I’ll refer to the original sources for more details and expert insights at the bottom of each chapter.

There’s always a chance I misunderstood something, made a mistake, or didn’t follow best practices or proper style. If you spot anything that’s wrong or could use some improvement, please let me know. Any feedback, suggestions, or bug reports are always appreciated!

Let’s get started, and enjoy!

Gerrit

Simple Image with PPM

The Portable PixMap (PPM) is a simple text-based image format. No special libraries needed: create an image by simply writing pixel data to a text file.

// ppm-example.cpp
#include <iostream>

int main() {
  int nx = 200;
  int ny = 100;

  std::cout << "P3\n" << nx << " " << ny << "\n255\n";
  for (int j = ny-1; j >= 0; j--) {
    for (int i = 0; i < nx; i++) {
      float r = float(i) / float(nx); // left to right from 0 to 0.995
      float g = float(j) / float(ny); // top to bottom from 0.99 to 0
      float b = 0.2;

      int ir = int(255.99 * r); // left to right from 0 to 254
      int ig = int(255.99 * g); // top to bottom from 253 to 0
      int ib = int(255.99 * b); // always 51

      std::cout << ir << " " << ig << " " << ib << "\n";
    }
  }
}

Simple Image with Javascript and Canvas

The Javascript code below is almost identical with the C++ code but instead of a PPM file stream it renders each pixel in a 200x100 canvas element.

To keep the color gradient in the same direction as the PPM I reversed the y-direction in the fillRect() call.

Further Reading

Next Steps