(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.
We set the z value of the image plane to zero.
As it happens, that is exactly the range of values
for vPos in the shader programs you are writing.
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,
in positive z.
Adapting the convention of photography, we use
the variable fl -- for focal length -- for the
distance of the observer from the image plane.
We say that observer is in location:
V = [0,0,fl]
To form a ray through any pixel [x,y] of the image,
we first take the different vector from that
pixel to 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 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);