Notes for Tuesday February 11 class -- Ray tracing to a sphere

 

Make sure you read through the notes below very carefully, and that you understand everything.

On Thursday we will be seeing what this looks like in GLSL shader code.

 

RAY TRACING TO A SPHERE
 

(1) Forming a ray for each pixel

Ray tracing is a very powerful rendering technique. The fundamental idea is to shoot a "ray" into the scene at every pixel of the image. In a sense, we are tracing light backwards from the camera into the scene.

Whatever object in the scene this ray hits first, that is the object visible at that pixel.

We can define a ray using an origin point V and a unit-length direction vector W.

Any point on the ray can be described by:

V + t W       for t >= 0
Note that points where t < 0 are not part of the ray, because those points are behind the ray origin V, not in front of it.

We can describe the image plane itself as a square floating in space. We can position the image plane in our virtual world so that x==-1 along its left edge, x==+1 along its right edge, y==-1 along ite bottom edge, and y==+1 along its top edge.

As it happens, that is exactly the range of values for vPos in the shader programs you are writing.

Adapting the convention of photography, we use the variable fl -- for focal length -- for the distance of the observer from the image plane. We therefore set the z value of the image plane to -fl.

We set the camera "pin hole" location V to be the origin:

V = [0,0,0]
Since the observer is in front of the square, and our coordinate system follows the right hand rule, the observer will be located on a point along the z axis that is more positive than the image plane.

To form a ray through any pixel [x,y,-fl] of the image plane, we first take the different vector to that pixel from the observer:

[x,y,-fl]
Then we normalize that vector to get the ray direction:
W = normalize([x, y, -fl])
If we were to write the same thing as shader language code it would look like this:
vec3 W = normalize(vec3(vPos.x, vPos.y, -fl));
Any point along this ray can be described by:
[Vx + t Wx, Vy + t Wy, Vz + t Wz], where t >= 0

 

(2) Finding the distance along the ray to a sphere

We can use our ray to "ray trace" to various objects in the scene. Suppose, for example, we want to ray trace to a sphere. To do this, we first need to come up with a good definition of a sphere shape. Since a sphere has a center location C and a radius r, we can define a sphere using the following four values:

Cx, Cy, Cz, r
To ray trace to this sphere, we need to find what point (if any) along the ray is on the surface of the sphere.

Points on the surface of the sphere are going to be those points that are a distance r from the center of the sphere.

Recall, from our definition of ● product, that the magnitude squared of any vector v is:

vx2 + vy2 + vz2
Using this fact, we can compute the distance squared from any point [x,y,z] to the sphere center by:
(x-Cx)2 + (y-Cy)2 + (z-Cz)2
So if a point [x,y,z] is on the sphere, it must be true that:
(x-Cx)2 + (y-Cy)2 + (z-Cz)2 = r2
Recall that any point along our ray can be described by:
[Vx + t Wx, Vy + t Wy, Vz + t Wz], where t >= 0
To make our math easier, let's shift coordinates, so that the sphere is at the origin [0,0,0].

To do this, we just need to replace V by

V' = V - C
This substitution moves the sphere center to the origin. Now our ray equation becomes:
[V'x + t Wx, V'y + t Wy, V'z + t Wz], where t >= 0
We can substitute this point into our equation for the sphere (which is now at the origin) to get:
(V'x + t Wx)2 + (V'y + t Wy)2 + (V'z + t Wz)2 = r2
Multiplying out the terms, we get:
t2 (Wx * Wx + Wy * Wy + Wz * Wz) +
t   (2 * (Wx * V'x + Wy * V'y + Wz * V'z) ) +
    (V'x * V'x + V'y * V'y + V'z * V'z - r2) = 0
Using our definition of dot product, we can rewrite this as:
(W ● W) t2 + 2 (W ● V') t + (V' ● V' - r2) = 0
But since W is unit length, (W ● W) is just 1. This further simplifies our equation to:
t2 + 2 (W ● V') t + (V' ● V' - r2) = 0
We can now solve the standard quadratic equation,
t = (-B +- sqrt(B2 - 4AC)) / 2A
where in our case:
A = 1
B = 2 (W ● V')
C = V' ● V' - r2
to get:
t = (-2(W ● V') ± sqrt(4(W ● V')2 - 4(V' ● V'-r2))) / 2
or:
t = -(W●V') ± sqrt((W●V')2 - V'●V' + r2)       Equation 1

 

(3) Finding the surface point and surface normal

The above equation can have zero, one or two real solutions, depending on whether the expression inside the square root is negative, zero or positive, respectively.

Zero real solutions means that the ray has missed the sphere.

If there is a real solution but t is negative, that means the sphere is behind the ray.

Otherwise, one solution means the ray is just barely grazing the sphere, and two solutions means the ray is going into the sphere at one point and then exiting out at another point.

If you do find two positive roots, then you want the smaller of the two roots, because that is the one where the ray enters the sphere.

Once you find t, you can find the intersection point P on the sphere surface by plugging t into the ray equation:

P = V + t W
Then in order to do lighting on the sphere, you can find the normalized vector from the center of the sphere to this surface point:
N = normalize(P - C)

 

To summarize the algorithm:

  • vec3 V = vec3(0, 0, fl);
  • vec3 W = normalize(vec3(vPos.x, vPos.y, -fl));
  • vec3 P = V - C;
  • Solve the quadratic equation above (Equation 1) to compute t
  • If first root t exists and is positive:
    • vec3 P = V + t * W;
    • vec3 N = normalize(P - C);