================================================================================== Understanding Ray-Plane Intersection ================================================================================== Let's walk through the logistics behind the ray-plane intersection test. To begin with, the equation derives itself from a ray-producing function which will take a vector and apply it as a directional offset from an arbitrary origin in the same coordinate space that the plane's origin offset is relative to. For the intersection to work, we know that the ray's direction has to be infinite, so we have no bound placed on our scalar; the scalar is, however, what we need to know. It may or may not come as a surprise to you that this formulae relies on properties which are known within the plane equation itself. Let's take a quick look at the plane equation: P * N + d = 0 Any point within the plane should be able to achieve a dot product with its plane normal that produces a value of zero, providing that the plane's distance from the world origin itself, d, is also applied as an offset. Why do we need this d value though? That is a good question, with a simple answer: a point in the world must always be relative to a given origin. In order to represent this point as a vector, we can draw a line from the given origin to the point. When taking the dot product between the point as it stands within the world, and the normal of a plane, there is no guarantee of perpendicularity however. Say, for instance, we have the following scenario: n| __|____.______ \ \ _|_ origin | There's a line approximately going from the middle of the location marked "origin" to a dot - this dot is the point on our plane, and origin is the location with which our values are relative to. Likewise, the 'n' denotes the vertical line it's adjacent to - the plane's normal. A dot product between two vectors deals heavily with angles, because the cosine of the angle between the two vectors plays a central role in the actual value it produces. The angle itself is produced by placing the vectors' tails against each other, and can be computationally obtained through the arccosine function (and ensuring that both vectors are normalized). Even though this knowledge is trivial for anyone who's studied algebraic geometry at levels more in depth than what's offered in a pre-calculus 2 course, it's also very significant: as our example point stands in the world, to take the dot product as it is now with the plane's normal would produce a value that would be calculated from the following perspective: p . \ n| \ | Vectors, as we know, do not represent positions. Rather, they represent directions which are associated with arbitrary lengths. We know that in order for a point to actually reside within the plane, the plane equation must produce zero when the point is "plugged in" to it. This point in particular involves a dot product, and in this case, the dot product between these two vectors will not produce a zero because a) neither of these vectors are zero vectors, and b) the only way a cosine will produce a value of zero is if its input is equivalent to 90 degrees, or pi over 2 radians. So, we don't have a zero, which leads us back to our initial point: n| __|____.______ _ \ | \ | |d| _|_ origin | | Our equation then might look something like: P * N + d = 0 <==> P * <0, 1, 0> + 3 = 0 ...in a 3D coordinate system where our X-axis increases from left to right, our Y-axis increases from bottom to top, and our Z-axis increases "out" of the screen you're reading. Notice the line we've added, and the |d| right next to it. The absolute value of d represents the distance from the origin's plane to the plane itself. You might see where this is going. Here's what happens when we remove this |d| from the plane's equation, and then bring the plane to our origin: n| __|____.___ _|_ origin | (It's not quite as simple, unfortunately, to draw a vector to the point using ascii characters when the plane is parallel with the x-axis) Which, in dot product land, means: n| P<----| (the arrow is just another way of depicting P's head) Thus, they're perpendicular. If you take a piece of paper and draw a plane in 2D or 3D with any kind of normal, regardless of its direction, and which is perpendicular to a plane of any distance from the world origin, we can use this method to prove that this equation works. It's important to note, though, that we need to move the plane 'd' units in the (positive or negative) direction of its normal so the the plane itself passes through the origin. As we've just seen, we can produce a vector from the origin to P's new location which will yield a value of zero when dotted with the plane's normal. If we have some ray, defined as R(t) = P0 + tV, where P0 is an arbitrary point in our world and V is a unit length vector with which t acts as a scalar for, we can use this definition of the ray within our point-plane equation: P * N + d = 0 <==> (P0 + tV) * N + d = 0 Solving for t: (P0 + tV) * N = -d (P0 * N) + (tV * N) = -d (P0 * N) + t(V * N) = -d [1] t(V * N) = -d - (P0 * N) t = -d - (P0 * N) / (V * N) = -(d + (P0 * N)) / (V * N) [1]: t(V * N) = (tV * N) is a valid property in this context, considering that V is assumed to be a vector of unit length such that |V| = 1. V * N = |V||N|cos(theta), where theta is the angle between V and N. Since |V| = sqrt(x^2 + y^2 + z^2) = 1, it follows that, |Vt| = sqrt((tx)^2 + (ty)^2 + (tz)^2) = sqrt(t^2(x^2) + t^2(y^2) + t^2(z^2)) = t * sqrt(x^2 + y^2 + z^2) = t * |V| = t --------------------------------------------- Our algebraic means of finding t is extremely simple, given that we know some basic properties. But *why*, geometrically speaking, does this work? In order to "think" in computer graphics, it can be very beneficial to look beyond the equation, and consider the logistics behind the environment the equation is supposed to being working with. Back to our equation: t = -(d + (P0 * N)) / (V * N) Let's take a visual route again with the following illustration: \ N\ _.*\ .* \ <- the plane \ P \ .----> V \ _|_o | - In this diagram, we have our normal N represented by a few dots. The dot closest to the plane is the tail, and the one farthest is the head. We can blame its ghetto simplicity and imaginative requirement on the limitations of ASCII. - P corresponds to the dot, which represents our world space point, and also the origin point of the ray. - V sits next to our unit length vector, which gives the ray its direction. - o corresponds to the set of "axes" which represent the origin of our coordinate space. \ N \ _.*\ .* \ P \ .----> V \ . \ . . _.|_o | As you can see above, we've added a dotted line to denote P as a vector. Our corresponding dot product for P and N is as follows: _.*. N.* . . . . . * . P What do we see here? In this case, they're perpendicular! This is a nice setup, here, because we have ourselves a fantastic mechanism for illustrating d's purpose overall. Thinking back to what we've learned from our foray into the plane equation, we know that d exhibits an *offset* which is proportional to the plane's distance from the origin. Because our dot product is 0, d's value is the only value which we have in the numerator. So, t = -(d + (P * N)) / (V * N) becomes, t = -d / (V * N) Setting aside the denominator and d's negative sign for a moment, we can scale V by d itself to see what it produces: \ N\ _.*\ * \ \ P \ .---------------> V \ . . \ . . . . _.|_o . ^ d's distance | Clearly, it's not quite enough to unravel the intersection point. How exactly does our denominator help, then? Consider how its computation is visually represented: |x | ----.----->V _.* *N The line underneath the marker, 'x', which lies between the two pipes is our cosine. Notice how it's a bit shorter than V and N. Since V actually hasn't been scaled yet, it's still a unit vector. So is N, for that matter. Which means that our cosine is *guaranteed* to be in the range [-1, 1]. We can tell here that it's somewhere in (-1, 0), using exclusive bounds. So, it's less than 0, but not enough to reach -1. Let's forget, for a moment, the sign of our cosine, and focus on the fact that its absolute value is < 0. Remember: we're *dividing* by this value. What happens when we divide a quantity by a value which is less than zero? The quantity's absolute value will be greater than its previous value before the division. So, in geometric terms, if V's direction was parallel to N, our additional adjustment of the denominator would effectively do nothing. And this makes sense, because the value of the cosine between two parallel unit vectors facing the same direction is 1. Likewise, two parallel unit vectors which face in opposing directions will result in -1. Which brings us to the sign of the numerator: it's negative. Without this sign, we would be given a negative value for t, because in our case, our ray's direction faces the front of the plane, which means that the plane's normal opposes it. A negative value of t, though, would negate the ray's direction; it wouldn't face in the direction of the plane, and therefore wouldn't actually be able to intersect it. Consequently, in situations where we wish to know whether a ray faces a given plane, we know that t < 0 implies that we're not facing the plane at all. To cement this fact in, consider what happens in a situation like this: N| __|__________ ^ . _|_ . | .P Here, our plane normal faces in the same direction as our ray. Obviously, we have a valid value of t here. Yet, the vectors are parallel. Let's take a closer look: N| __|__________ ^ . _|_ origin . . | . * ^ direction to P from origin Combining the two vectors... . <-- P's tail N| . | * .p Ahh, yes: the tails aren't connected. We need to adjust: |N | . * .p Regardless of whether or not we view N as the initial or terminating axis in our "circle", here, we'll have a cosine of -1. And that's that.