GLSL Signed Distance Function for a Round Cone
This article provides a complete reference and technical implementation of the signed distance function (SDF) for a round cone in GLSL. It covers the underlying geometric principles of tapering a segment between two spheres of different radii, details how the 3D distance problem is reduced to a 2D profile evaluation, and provides optimized, production-ready shader code for both axis-aligned and arbitrarily oriented round cones.
Understanding the Round Cone
A round cone is defined as the convex hull of two spheres with centers \(a\) and \(b\) and radii \(r_1\) and \(r_2\). It functions essentially as a capsule (tapered cylinder) whose ends smoothly transition into spherical caps.
Because a round cone possesses rotational symmetry around the central axis connecting its two sphere centers, the 3D distance query can be reduced to a 2D cross-section. The query point \(p\) is evaluated against three Voronoi regions:
- The bottom spherical cap (radius \(r_1\)).
- The top spherical cap (radius \(r_2\)).
- The conical tangent surface connecting the two spheres.
Axis-Aligned Round Cone Implementation
When the cone is oriented along the Y-axis, starting at the origin \((0, 0, 0)\) with base radius \(r_1\) and extending to height \(h\) with top radius \(r_2\), the 2D projection simplifies to the radial distance \(q_x = \sqrt{p_x^2 + p_z^2}\) and axial height \(q_y = p_y\).
float sdRoundCone(vec3 p, float r1, float r2, float h)
{
// Reduce 3D problem to a 2D profile
vec2 q = vec2(length(p.xz), p.y);
// Compute geometric tangent parameters
float b = (r1 - r2) / h;
float a = sqrt(1.0 - b * b);
// Project q onto the tangent direction
float k = dot(q, vec2(-b, a));
// Bottom sphere region
if (k < 0.0) return length(q) - r1;
// Top sphere region
if (k > a * h) return length(q - vec2(0.0, h)) - r2;
// Conical surface region
return dot(q, vec2(a, b)) - r1;
}Explanation of Variables
b: Represents the sine of the cone angle: \(\sin(\theta) = \frac{r_1 - r_2}{h}\).a: Represents the cosine of the cone angle: \(\cos(\theta) = \sqrt{1.0 - \sin^2(\theta)}\).k: The scalar projection testing which side of the tangent boundary lines the point falls into.
Arbitrary Orientation Round Cone Implementation
For arbitrary endpoints \(a\) and \(b\) in 3D space, computing coordinate system transforms per point can be avoided by utilizing vector projections along the axis \(\vec{ba} = b - a\).
float sdRoundCone(vec3 p, vec3 a, vec3 b, float r1, float r2)
{
vec3 ba = b - a;
float l2 = dot(ba, ba);
float rr = r1 - r2;
float a2 = l2 - rr * rr;
float il2 = 1.0 / l2;
vec3 pa = p - a;
float y = dot(pa, ba);
float z = y - l2;
// Radial offset squared multiplied by l2
vec3 v = pa * l2 - ba * y;
float x2 = dot(v, v);
float y2 = y * y * l2;
float z2 = z * z * l2;
// Check separation boundaries
float k = sign(rr) * rr * rr * x2;
if (sign(z) * a2 * z2 > k) return sqrt(x2 + z2) * il2 - r2;
if (sign(y) * a2 * y2 < k) return sqrt(x2 + y2) * il2 - r1;
// Cone body surface
return (sqrt(x2 * a2 * il2) + y * rr) * il2 - r1;
}Mathematical Breakdown
ba: The displacement vector representing the central axis of the cone.l2: The squared length of the axis vector \(\|ba\|^2\).rr: The difference between the two radii (\(r_1 - r_2\)).a2: The squared base of the tangent right triangle, derived from the Pythagorean theorem: \(\|ba\|^2 - (r_1 - r_2)^2\).x2,y2,z2: Squared components scaled by \(l_2\) to delay expensive square root calculations until the region branching is resolved.