Homogeneous coordinates

Represent a point in homogeneous coordinates, by [x,y,z,w], which is used to represent the physical location [x/w,y/w,z/w].

"Points at infinity" are just directions, represented by [x,y,z,0].

So a coordinate transformation could consist of x, y and z direction vectors and a position vector as the translation. These can be written in a matrix as column vectors, such as the identity matrix below:

x y z t

Forming a ray

Computing V

A simple place to put the camera eye point is along the positive z axis, so we can look at objects at the origin. If we place this eye point a distance of fl away (where "fl" is short for "focal length"), then we get:

V = [0.0, 0.0, fl, 1.0]

The homogeneous coordinate of V is 1.0, which indicates that V is a point, not a direction.

Computing W

We want the ray direction W to "scan" the image, as u and v each vary from 0.0 to 1.0. For any given value of [u,v], we can define a ray direction:

W = normalize([u-0.5, 0.5-v, -fl, 0.0])

Ray tracing to a sphere

Equation of a sphere, centered at c, with radius r:
(x-cx)2 + (y-cy)2 + (z-cz)2 - r2 = 0

Substitute components of ray equation into sphere equation:


x → Vx + t Wx
y → Vy + t Wy
z → Vz + t Wz
(Vx + t Wx - cx)2 + (Vy + t Wy - cy)2 + (Vz + t Wz - cz)2 - r2 = 0
which equals:
(t Wx + (Vx - cx))2 + (t Wy + (Vy - cy))2 + (t Wz + (Vz - cz))2 - r2 = 0
Regrouping terms, we get:

t2 ( Wx2 + Wy2 + Wz2 ) +

t ( 2 ( Wx (Vx - cx) + Wy (Vy - cy) + Wz (Vz - cz) ) ) +

(Vx - cx)2 + (Vy - cy)2 + (Vz - cz)2 - r2 = 0

which equals:
WW t2 + 2W•(V-c) t + (V-c)•(V-c) - r2 = 0

So we need to solve the quadratic equation for:

A = WW

B = 2W•(V-c)

C = (V-c)•(V-c) - r2

where the quadratic equation is:
t = (-B ± B2-4AC) / 2A
Since w is normalized, the value of WW is always 1.0, so the quadratic equation in this case simplifies to:
t = (-B ± B2-4C / 2

Interpreting the results:

If there are no real roots, then the ray has missed the sphere.

Otherwise, the smaller of the two roots is where the ray enters the sphere, and the larger of the two roots is where the sphere exists the sphere.

Using the root value to find the surface point:

Once we have found the smaller root t, we can substitute it back into the ray equation to get the surface point S:

S = V + t W

Simple lighting model:

Lights at infinity (like the Sun)

We are going to assume for now that light sources are infinitely far away, or at least far enough away that they can be considered infinitely far for practical purposes, like the Sun, which is 93 million miles from Earth.

This means that the direction vector L to the light source will have the same value for all points in the scene:

L = [ Lx, Ly, Lz, 0 ]

Lambert shading

We are going to assume for now that the surface is a perfect scattering diffuser, like moondust. In this case, incoming light from any direction scatters equally out to all directions. Such a surface is called Lambertian.

The brightness of a Lambertian surface depends only upon the cosine between its surface normal and the direction toward the light source.

Computing the surface normal

For a sphere, it is easy to compute the surface normal. It is simply the unit length (ie: normalized) vector from the center of the sphere toward the surface point:

n = normalize(s - c)
Once we know the surface normal vector n, then we can calculate Lambertian reflectance by:
[r,g,b] * max( 0, nL )

Where [r,g,b] is the color of the surface. There is usually some ambient light bouncing around in the scene in all directions. The above equation can be modified to account for an approximation to that ambient light:

[r,g,b] * (A + (1.0 - A) * max( 0, nL ))
Ambient reflectance is typically set to a low value, such as 0.1.


Physical model: clear plastic with embedded dye particles

There are many types of materials. In class we discussed one common type: plastic with embedded dye particles. Photons that penetrate the plastic will bounce off multiple particles. If the photon is not absorbed, it emerges in a random direction, to create Lambertian reflectance.

Those photons that simply bounce off the surface will create mirror-like reflections, if the surface of the plastic is smooth. As we discussed in class, this portion of the surface reflectance can be handled by computing a reflection ray, and then ray tracing again into the scene starting from the point of emergence.

Computing the reflection vector

As we derived in class, given an incoming ray direction W and a surface normal direction n, we can calculate the emergent reflection direction by:

R = 2 (-Wn) n + W

Shooting the ray (no recursion in shader programs)

We can therefore form a new ray, starting a small distance ε outside the sphere surface, as:

V' = S + ε R

W' = R

You should use a small positive value for ε, such as 0.001.

Adding in the reflection

The reflected ray can simply be multiplied by some [r,g,b] color and added to the final color of the surface point.

Note that you cannot have the ray tracer call itself recursively in the shader, because WebGL shader programs which run on the GPU do not permit recursive function calls. But you can call the ray tracer for the primary ray, and then call it again to compute a reflection ray. That is the method I used for the reflecting spheres I showed in class.


Shooting the shadow ray

Shadows are a bit like reflections, but simpler. To compute whether a surface point is in shadow, simply shoot a ray toward the light source. If any object is hit by the ray, then the surface point is in shadow. The shadow ray is formed as follows:

V' = S + ε L

W' = L

Using the result

The effect of a surface point being in shadow is that the point will have only ambient reflectance [Ar,Ag,Ab].

Changing the view:

Using a different initial V and W

As I showed in class, you can change or animate the "camera" view into the scene by choosing different initial values for V and W. I leave it as an exercise for you to figure out how you might do this.


Implement a basic ray tracer with a scene of two or more spheres, each of which exhibits Lambertian reflectance.

For extra credit, try implementing reflection, shadows or any other feature you think is cool or challenging. For example, you might try to figure out how to create a form of surface texture.

Here is an updated version of script.js, which contains the "reload with changes" button I showed in class this week. Feel free to use this new version in place of the one I distributed last week.