Ray – Plane
One of the most fundamental algorithms in 3D games is being able to calculate the arbitrary distance between a point with direction and a plane.
A few key concepts to understand is:
- The plane is infinite in length
- Unless the ray is parallel to the plane, the ray will eventually hit the plane (due to it being infinitely long)
- Despite the guarantee of hitting the plane the ray still may hit the plane behind the point you started your ray from.
You can see we have 4 Vectors:
Ray Origin – Is where the ray is starting from (I.E the end of the barrel in a gun)
Ray Direction – Is a normalised vector describing which direction the ray points
Plane Normal – The inverse slope of the plane in normalised length (similar to the Ray Direction)
Plane Translation (Plane Origin) – I say translation because if the plane wasn’t positioned it would always intersect at the origin, so it has to be moved in the world to where we want our plane to be, Its best not to think it as a point (as the plane has infinite points along it) but a translation of the whole plane from the origin.
Mathematicians at this point would love to get out their algebra and differentiate the equations to find out when these 2 ‘lines’ meet, but it computer graphics it’s often way too slow and cumbersome to bother with algebra solving. Instead we stick to the basics of physics and maths.
We start of by using a Dot product:
Basically the dot product can be used to tell us how 2 normalized vectors converge with each other, in that if a dot product gives 0 it means the both vectors are perpendicular to each other (I.e. not moving towards one another), if -1 or 1 they are parallel (Moving towards or away each other at full ‘speed’), and all values in-between show the rate at which they are pointing to each other. Remember this is a scalar value, so think in 1 dimension:
If we dot product the Ray direction with the Plane Normal, we get a scalar value of how the direction is moving towards the plane, if it is 0 we know the direction is parallel to the plane (90 degrees to the plane’s normal), and thus the ray will never hit the plane (We can stop the detection here if this is the case). However any value other than 0, the direction vector is moving at that amount towards the plane, and will hit eventually.
Anyone who has experience in physics will know the basic equation of Distance = Speed * Time.
We can use this to find out when our Ray is going to hit the plane, if it takes negative time, we know the origin of the Ray started past the plane and wont intersect it.
But our ray does not have speed?
This is true, but speed is a fancy word for Rate of change, so by substituting the scalar value from the dot product, you get the Rate of change towards the plane.
Because of this, it is important to note that Time should not be considered ‘time’ at all, but rather a value that represents how much ‘rate of change’ we need to cover a distance (distance/speed).
Now you might think you can get the distance by subtracting the plane origin and ray origin. However, the plane is infinite in length, and the plane origin is actually just a random point on the plane – which could be anywhere. What we need is the point on the plane that is closest to the ray origin, then we can measure the time it takes to travel this distance.
We can actually skip a step at this stage and directly work out the correct distance to the closest point by using the dot product of the Plane normal and origins.
DistanceToNearestPointOnPlane = Dot(PlaneNorm, PlaneO – RayO)
SpeedTowardsPlane = Dot(RayDir,PlaneNorm)
Substituting and rearranging:
T = D/S
Time = DistanceToNearestPointOnPlane / SpeedTowardsPlane
This Time value will now represent the amount of RayDir needed to get between the RayOrigin and the Plane.
- If its negative, the Ray origin was behind the plane, and so the Ray does not intersect the plane
- If it is positive, a collision occurs, at this value along the ray.
To get the exact point at which the ray hits the plane is easy:
RayOrigin + (Time * RayDir) = Collision Point (XYZ)
The above image shows how the calculation works if Time was equal to 3. The blue dot being the RayOrigin. (It has been flattened into 1 Dimension for illustration)
If your RayDirection is of unit length, you can directly compare lambda/time values from multiple Ray-Plane calculations to see which occurs first.
Also this works for 2D lines EXACTLY the same way! just omit the Z value in calculations or leave it 0;