a simple ray tracer in PHP

When you want to render a 3D model of a world, you need to project every element onto a 2D plane (the computer screen). If you have a single planner object (say a triangle or a square), it's pretty easy to compute the projection. Things get a little complicated when you have more than one oject, where objects can partially hide each other. This problem is called the "rendering" problem.

There are several rendering algorithms: you can paint every object individually and keep track of the depth of each pixel. You can then filter these pixels and only keep the cloest one. This is called z-buffering.

Another way to render a 3D scene is to break each model into triangles. You can then use some efficient algorithm to render the triangles (a technique used by most graphics cards).

Finally, you can simulate the physics of light. This technique is called ray tracing. Ray tracing is interesting to implement just for fun, because it's pretty simple and gives nice results. The downside of ray tracing is that it computationally expensive. This means it's a slow rendering algorithm. It can be used when creating animated movies, but is not very suitable for real time applications.

This page explains how I implemented a very simple ray tracer in PHP (around 1500 lines of code). The code is open source and available on my github.

The world is a simple containers for various kinds of objects:

All the renderers share some common logic (renderer.php). The core of the ray tracer is the render() method, which "casts" rays from the camera towards the plane of projection. For each ray, we call rayIntersection() to see if the ray intersects with any object.

The renderer returns a color, which is used to mark the pixel at the specific x,y coordinate on the projection plane.

Once we have computed all the pixels, we save the result to an image file. I implemented a bmp encoder for fun (bmp_encoder.php), but the GD library provides similar functionality (gd_encoder.php).

I organized the code in a way that lets me add new kinds of objects. Each object needs to implement its formula for computing intersections with rays.

The sphere-ray intersection code therefore lives on in the sphere class (sphere.php). The intersection is computed using two dot products on Vectors.

In a similar way, the plane-ray interscetion code lives in the plane class (plane.php). The intersection is computed using three dot products on Vectors.

Simple rendering of two spheres on a floor (sample_01.php). Took ~3.5 seconds to render.

Flat rendering of two spheres on a floor (sample_03.php). Took ~6 seconds to render.

Diffuse rendering of two spheres on a floor (sample_05.php). Took ~7 seconds to render.

Phong rendering of two spheres on a floor (sample_07.php). Took ~7.5 seconds to render.

Having more kinds of objects (boxes, cones, cylinders, lathe, torus, triangle, discs, etc.) would allow the rendering of more interesting scenes.

A fun next step would be to extend this simple ray tracer to compute refraction too (i.e. have transparent objects).