Ray tracing is a method of generating images by 'tracing' or following
light rays as they are reflected and transmitted around a scene. By
tracing rays, you can generate realistic reflections and simulate
realistic shading.
In practice, it would be too time consuming to send all of the rays from
the light sources, and then see if they enter the eye
Tracing rays and calculating the colors for every pixel requires the use
of vector math, and quite a bit of processing power. To provide a little
background for one of the vector equations required, here is the vector
equation for a line:
p = t * (p1 - p0) + p0
or
p = t * v + p0
Where p1 and p0 are two points that define the vector direction of the
line, v is the vector defined by p1 - p0, and t is a scalar value that
makes the point p move along the line (When t is 0, the point p will be
located at p0, and when t is 1, p will be located at p1).
This vector equation of the line is the basis for ray tracing, as all of
the rays are straight lines. To generate the rays, an
eye point is chosen, and for a ray is sent out from
the eye through each of the on-screen pixels, to see if each intersects
with any objects.
Note: The intersection test must be performed with many objects
(possibly all objects in the scene), to find the closest intersection
point to the 'eye' (as any intersections behind it are blocked).
One of the easiest objects to calculate intersections for is the sphere.
To calculate the intersection, you use the vector equation of the line in
the vector equation for a sphere, and solve the quadratic equation for
the roots (the intersection points, in terms of 't').
Here is the vector equation of a sphere:
(p - C) . (p - C) - R2 = 0
Where p is any point on the surface, C is the center of the sphere, R is
the radius of the sphere, and '.' means dot product.
Using the vector equation of the line in the equation for a sphere gives:
((t * v + p0) - C) . ((t * v + p0) - C) - R2 = 0
Since we want to solve for t, we need to regroup the equation as follows:
(t * v + (p0 - C)) . (t * v + (p0 - C)) - R2 = 0
Expanding the equation and grouping terms gives the following:
t2 * (v . v) + 2 * t * (v . (p0 - C)) +
((p0 - C) . (p0 - C) - R2) = 0
From here, we can apply the quadratic equation to solve for the roots
(t):
(-b +/- (b2 - 4 * a * c)0.5) / (2 * a)
Where we use a = (v . v), b = (2 * (v . (p0 - C))),
and c = ((p0 - C) . (p0 - C) - R2).
To do a quick intersection test (to see if there are any non-imaginary
intersections with the sphere), we check to see if (b2 - 4 * a
* c) is less than epsilon. Where epsilon is some low tolerance number
(see floating point imprecisions for reasons
why).
All colors of light can be defined with amounts of red, green, and blue
components. Because of this, all calculations done for coloring will be
done with an RGB triplet (red, green, blue). Multiple colors will need
to be added up to calculate the color values for any given point (or
pixel); if, when the colors are added up, any of them are beyond the
maximum intensity, they should clipped to (cut off at) the maximum
intensity.
For my purposes, I use floating point numbers in the range of 0 to 1 to
represent any of the values in the RGB triplet (this is fairly standard
in graphics). If any of the values calculated are below 0 or above 1,
they should be set to 0 or 1 respectively.
The amount of illumination on an object depends on the angle of the
object with respect to the light rays coming in contact with it.
This allows us to do some very simple shading (known as
diffuse shading because of the type of lighting it
models) using only the normal of the surface (at the point of
intersection), and the 'light' vector (the vector from the point of
intersection to the light source that is contributing to the
illumination).
Since the angle is important, all we need to do is take the dot product
of the 'light' vector (normalized) and the normal of the surface (also
normalized). This will result in a value between -1 and 1. If the value
is less than 0 (or epsilon), then the light would
be illuminating the back side of the surface. For most purposes, the back
side of the surface should not be visible, so the value should be set to
0.
Given the scalar value (now between 0 and 1), we can just multiply this
by the RGB triplet that defines the light color. This gives the
'intensities' of the RGB components of the light hitting the surface. To
calculate the color reflected from the surface, we just multiply this
value by the 'diffuse' color of the surface, resulting in the color
'reflected' from that point of the object.
Note: Any given light may or may not contribute to the illumination of
the point, so a shadow ray must be sent first to
determine if the given light source contributes.
Floating point calculations have a finite amount of precision. Because
of this, problems enter with 'round-off' errors, and values not exactly
equaling zero (numbers which should be zero could actually be stored as
some very small number). Thus, any compares of floating point values
should be done with a small 'epsilon' value, instead of zero. This will
eliminate some of the errors generated by using finite precision
numbers. Good values for epsilon depend on both the floating point
format used for the given platform and the 'size' of the data being used
(it should be much smaller than the radius of any sphere, or the distance
between any objects).
Floating point imprecisions will also cause artifacts in ray traced
images when sending 'shadow rays', reflected rays, or transmitted rays.
This problem arises when the ray that leaves the surface of the object
intersects with the object at the same point (or very close to the same
point) from where the new ray originated. This is due to the same
problem with finite precision (discussed above), and can cause artifacts
that look like small holes or speckles.
To prevent this problem, every ray which leaves the surface of an object
should have the 'origin' translated by the epsilon value, in the
direction of the ray. That is:
new origin = epsilon * direction + origin
Using this this method should eliminate holes and speckles in the ray
traced image.