In this blog post I will show you how to ray trace a torus. I will assume that you already know how to ray trace simple shapes like spheres and cubes. I will also assume some basic familiarity with shading and ray tracing in general.

### Obtaining torus equation

Before we start I must introduce some terminology. I will use $R$ to denote torus major radius (the distance from the center of the tube to the center of the torus), and $r$ to denote torus minor radius (the radius of the tube).

Let us consider torus $T$ centered at point $(0,0,0)$ with radiuses $R$ and $r$. Torus $T$ can be defined as a set of points for which certain function $F$ returns zero:

Our task will be to find a suitable definition of function $F$ that properly describes torus $T$.

We will start by looking at the intersection of torus $T$ with $XY$ plane: Every point $P=(x,y)$ on the circumference of the right circle satisfies equation:

Now imagine that we are taking some point $P$ on the circumference and we are rotating it around $Y$ axis. This way point $P$ becomes a set of points in 3D space:

Torus can be obtained by rotating all points on the circumference of the circle. In other words points $(x',y,z')$ on the surface of a torus satisfy equations:

This equations can be simplified by removal of $x$ variable into:

And this is exactly what we were looking for, a suitable definition for our function $F$. After a bit of renaming ($x' \rightarrow x$, $z' \rightarrow z$) we can write our final equation:

### Solving torus equation

Given ray definition:

where $o$ is the ray origin (starting point) and $d$ is a unit vector ($\left\lVert d \right\rVert = 1$) that represents the ray direction, we will try to find all positive ($t > 0$) solutions to the equation:

Notice that for a particular ray this equation can have 0, 1, 2, 3 or 4 solutions:

We will start by substituting $x, y, z$ variables by $r(t)$ point components in the formula of function $F(x,y,z)$:

And then we expand them to their full definition:

After long and tedious calculations and a lot of grouping and simplifications we finally get:

where

Since our equation is just a polynomial of 4th degree, we may use one of the standard algorithms to solve it. In my demo application I used algorithm from Graphic Gems book, freely available at GitHub. But you are free to use any other algorithm. In particular Numerical Recipes in C book is a good source of numerical algorithms.

TIP: Unfortunately Roots3And4.c file from Graphic Gems (called solver.js in my demo app) is sparsely documented. If you want to know how finding roots of 4th degree polynomial actually works please read section from Wikipedia about Ferrari method, but use second definition of the resolvent cubic described in this Wikipedia article. With a bit of effort you should be able to follow and understand source code of the solver then.

### Practical considerations

1. Currently we can only render tori centered at point $(0,0,0)$ and laying on $XZ$ plane. To obtain tori located at arbitrary points and/or in arbitrary positions, we must apply matrix transformations to the ray just before computing intersections with the torus surface. For example from the viewer point of view the results of the following operations are the same: translating ray by vector $(0,2,0)$, translating torus by vector $(0,-2,0)$. In my demo app this transformation is done in RayTracer.js#rotationEnd method. For more details please see Ray Tracing from the Ground Up book.

2. Due to limited accuracy of the floating point computations artifacts may be seen when we use huge numerical values for torus radiuses. For the best results we should keep $% $. If you need huge tori in your scenes please use ray transformation technique described in point (1) to scale small tori as needed.

#### Source code

As a part of preparations to write this post I created a simple demo app that ray traces a torus:

Application is written in JavaScript. To run demo app execute:

npm install command may take a while to finish, so please be patient. Also notice that you have to had both bower and gulp installed on you local machine to make this work. You can install them by executing: