“I know what you're thinking, 'cause right now I'm thinking the same thing.
Actually, I've been
thinking it ever since I got here: Why oh why didn't I take the BLUE pill?”
Object File Format
Specifying a capsule in our scene file is quite simple. We need only to
represent
our object in the program and so very little information is required. In the
case
of a capsule we are able to re present this needed information in a number of
different ways but for easy of computation and understanding the following
format was used:
<Ax, Ay, Az> <Bx, By, Bz> radius
Here, A specifies the location of the Base cap (position of the first sphere
of the
capsule) and B specifies the location of the Top cap (2nd sphere of the capsule).
Radius is self explanatory, being the radius of both the end caps and cylinder.
With this format it does not matter which position is the Base and which is the
Top.
Materials are defined in the same way as every other
object in the scene format.
Capsule Intersection
Intersection with a capsule is pretty straightforward.
Each end is defined by a
sphere and is trivial to test. The fol lowing briefly covers it:
We plug the ray equation into our sphere equation and
solve the resulting
quadratic equation . Our coefficients end up being:
Scalar A = Dot( ray.direction, ray.direction );
Scalar B = 2.0 * Dot( ray.origin – capsule.A, ray.direction );
Scalar C = Dot( ray.origin – capsule.A, ray.origin – capsule.A) –
(capsule.radius)^2
These are our coefficients for the quadratic formula, we
solve for both values of
t . One will be our first intersection time along the ray, the other the second
intersection time (sphere contains a possible two intersections ).
Using our found time t, we store our relevant information
into an Intersection
structure for future reference (point of intersection, time of intersection,
normal,
material).
This is done for both the Base and Top sphere of our
capsule. By now we should
have two Intersection structures filled with relevant information (containing
either intersections or NO intersections).
Intersection with the cylinder follows and requires a bit
more comprehensive
knowledge of 3 D math :
First we must use the information we have to construct
relevant vectors to solve
the parametric equation created by plugging the ray into the cylinder equation:
Vector AO = ray.origin – capsule.A
Vector BO = ray.origin – capsule.B
Vector AB = capsule.B – capsule.A
Vector AOxAB = AO x AB
Vector VxAB = ray.direction x AB
Now we may find our coefficients for the quadratic
formula:
A = Dot( VxAB, VxAB )
B = 2.0 * Dot( VxAB, AOxAB )
C = Dot( AOxAB, AOxAB ) – Dot( AB, AB ) * (capsule.radius*capsule.radius)
Solve the quadratic for our two times, t.
De termining which t to use is trivial and is the same as
any intersection test, and
now we have a filled out Intersection struct for the cylinder.
But wait! There is one last trick to intersecting with our
cylinder. That is we’ve
only solved for an intersection with an infinite cylinder defined by the axis
AB.
We need our spheres to be the ends and not to draw the rest of the infinite
cylinder! These checks are trivial and require that we know our intersection
point before storing.
Vector intersection.pt = ray.origin + ray.direction * t
Our intersection point on the cylinder is only valid if
between our Base and Top
sphere positions (endpoints of the cylinder). To check that our point is ‘above’
our base:
If Dot( intersection.pt – capsule.A, AB ) > 0
And to check that our point is ‘below’ our top:
If Dot( intersection.pt – capsule.B, AB ) < 0
Trivial!!
The normal of the end cap intersections is trivial (sphere
normal):
ANormal = intersection.pt – capsule.A
BNormal = intersection.pt – capsule.B
To calculate the correct normal for the cylinder we must
find, using 3D math, the
point along the cylinder axis that gives us:
Vector axisvec = intersection.pt – normal
First we project our Base to intersection point vector
onto our cylinder’s axis:
Scalar proj = Dot( intersection.pt – capsule.A, AB )
Catch what needs to happen here? Our vector AB must be
normalized!! We are
projected onto a direction, we don’t necessarily care about AB’s magnitude. If
we fail to normalize AB we’re in for a very wrong normal calculation.
Now we need the point along the axis that we wish to find
our normal with. We
have the projected length, now we must simple multiply that with our axis
vector:
axisvec = AB * proj
Note, axisvec is a vector with the direction of AB but
scaled to the ‘height’ of our
intersection point (much like finding the center of a sphere). Finishing the
calculation, to find the resulting cylinder normal we subtract this vector from
our
original, we may use the formula given 3 steps above :
CNormal = (intersection.pt – capsule.A) – axisvec
Altogether:
CNormal = (intersection.pt – capsule.A) – AB * Dot(
intersection.pt – capsule.A, AB)
What’s our final intersection with the capsule? Out of the
three intersections
calculated (Base cap, Top cap, Cylinder), our intersection is the one with the
smallest time t value.
The resulting intersection points and normal calculations
for the capsule work for
a capsule of any length in any direction, no matter the Base or Top.