Ray-Sphere Intersection Quadratic Equation in GLSL
This article explains the mathematical formulation and GLSL implementation of the quadratic equation used to find the intersection points between a ray and a sphere. By combining the vector equations of a 3D line and a spherical surface, we derive the standard quadratic equation \(at^2 + bt + c = 0\), analyze its discriminant to evaluate hit conditions, and demonstrate how to implement the solution efficiently inside a GLSL shader.
Mathematical Formulation
A ray is defined parametrically by an origin point \(\mathbf{O}\) and a normalized direction vector \(\mathbf{D}\), parameterized by distance \(t\):
\[\mathbf{P}(t) = \mathbf{O} + t\mathbf{D}\]
A sphere is defined by its center point \(\mathbf{C}\) and radius \(r\). Any point \(\mathbf{P}\) on the surface of the sphere satisfies:
\[\|\mathbf{P} - \mathbf{C}\|^2 = r^2\]
Substituting the ray equation into the sphere equation yields:
\[\|(\mathbf{O} + t\mathbf{D}) - \mathbf{C}\|^2 = r^2\]
Let the vector from the sphere center to the ray origin be \(\mathbf{V} = \mathbf{O} - \mathbf{C}\). The equation expands via the dot product:
\[(t\mathbf{D} + \mathbf{V}) \cdot (t\mathbf{D} + \mathbf{V}) = r^2\]
\[t^2(\mathbf{D} \cdot \mathbf{D}) + 2t(\mathbf{D} \cdot \mathbf{V}) + (\mathbf{V} \cdot \mathbf{V}) - r^2 = 0\]
The Quadratic Equation
The expansion matches the standard quadratic form \(at^2 + bt + c = 0\), where:
- \(a = \mathbf{D} \cdot \mathbf{D}\) (which simplifies to \(1.0\) if \(\mathbf{D}\) is normalized)
- \(b = 2.0 \cdot (\mathbf{D} \cdot \mathbf{V})\)
- \(c = (\mathbf{V} \cdot \mathbf{V}) - r^2\)
The discriminant \(\Delta = b^2 - 4ac\) determines the nature of the intersection:
- \(\Delta < 0\): The ray misses the sphere (no real roots).
- \(\Delta = 0\): The ray is tangent to the sphere (one real root).
- \(\Delta > 0\): The ray enters and exits the sphere (two real roots).
When \(\Delta \ge 0\), the distances along the ray to the intersection points are:
\[t = \frac{-b \pm \sqrt{\Delta}}{2a}\]
To optimize computations in GLSL, shaders frequently substitute \(b = 2h\), where \(h = \mathbf{D} \cdot \mathbf{V}\). This cancels the factor of 4 in the discriminant and the factor of 2 in the denominator, resulting in:
\[\Delta' = h^2 - ac\] \[t = \frac{-h \pm \sqrt{\Delta'}}{a}\]
GLSL Implementation
Assuming the ray direction rayDir is normalized, \(a = 1.0\), which further simplifies the
computation.
// Returns true if an intersection occurs, setting t to the nearest valid hit distance
bool intersectRaySphere(vec3 rayOrigin, vec3 rayDir, vec3 sphereCenter, float radius, out float t) {
vec3 oc = rayOrigin - sphereCenter;
// Coefficients (with normalized rayDir, a = 1.0)
float h = dot(rayDir, oc);
float c = dot(oc, oc) - radius * radius;
float discriminant = h * h - c;
// Ray misses the sphere
if (discriminant < 0.0) {
return false;
}
float sqrtD = sqrt(discriminant);
// Check nearest hit point first
float t0 = -h - sqrtD;
float t1 = -h + sqrtD;
if (t0 > 0.0) {
t = t0;
return true;
} else if (t1 > 0.0) {
t = t1; // Inside the sphere
return true;
}
// The sphere is behind the ray origin
return false;
}This simplified form minimizes arithmetic operations, branch costs, and register pressure, making it optimal for ray marching and path tracing pipelines within fragment shaders.