Skip to main content.

Ray/disc intersections

A ray/disc intersection is just the combination of two other tests. We check for an intersection between the ray and the infinite plane that contains the disc; if such an intersection exists, then we check whether the hit position lies on the disc, using a simple distance check.

I've already written about ray/plane intersection tests, so this article will build on those. I've also provided code at the bottom as a public domain work.

The distance check

Let's review our ray/plane check:

Do
=
origin of the disc
Dn
=
disc surface normal
Ro
=
the ray's origin
Rd
=
the ray's direction
Hp
=
the position at which the ray hits the infinite plane containing our disc
Hd
=
the hit distance: the distance from the ray origin to the hit position
Hp = Ro + RdHd Hd = ((Do - Ro) • Dn) ÷ RdDn

We'll supplement that with a distance check:

Dr
=
radius of the disc
||Hp - Do||Dr

On its own, this is actually a check to see if the hit position lies inside of a sphere centered on Do with radius Dr. If the hit position is inside of that sphere and on the infinite plane that contains our disc, then it must be on the disc.

Sadly, there's no way to avoid having to do the math for the hit position. Ah, well. We can at least save a square root operation (good for computers) if we compute the check like this, remembering that the dot product of a vector with itself is the vector's length squared:

||Hp - Do|| squared Dr squared  (Hp - Do) • (Hp - Do) ≤ Dr squared 

Free code!

The following C++ code is licensed under CC0 (full legal text / summary), and so is effectively a public domain work. You can incorporate it into your programs without the need for attribution, payment, or similar. (That said, linking back here would be polite!)

This code makes use of the GLM library for vector math. It also depends on my ray_plane_intersection function.

// Compute the intersection of a ray and a disc. The ray direction must be normalized. 
// Returns the hit distance, from the ray's origin.
bool ray_disc_intersection(
   const glm::vec3& ray_origin,
   const glm::vec3& ray_direction, // must be normalized

   const glm::vec3& disc_origin,
   const glm::vec3& disc_normal,
   float radius,

   float& hit_distance
) {
   float Hd;
   bool  plane = ray_plane_intersection(ray_origin, ray_direction, disc_origin, disc_normal, Hd);
   if (!plane)
      return false;
   glm::vec3 Hp = ray_origin + ray_direction * Hd;
   glm::vec3 Dd = Hp - disc_origin;
   if (glm::dot(Dd, Dd) > radius * radius)
      return false;
   //if (Hd < 0) // redundant with checks done in ray/plane
   //   return false;
   hit_distance = Hd;
   return true;
}