Finite Cylinder Signed Distance Function in GLSL
This article provides a concise technical breakdown of how to construct and implement the signed distance function (SDF) for a finite cylinder in GLSL. You will learn the underlying mathematical formulation that splits distance evaluation into radial and axial components, followed by the complete, optimized GLSL shader code suitable for raymarching pipelines.
Mathematical Formulation
A finite cylinder centered at the origin and aligned with the Y-axis is defined by two parameters: its radius \(r\) and its half-height \(h\).
Evaluating the signed distance from an arbitrary point \(p = (x, y, z)\) to the cylinder surface requires decoupling the problem into two orthogonal dimensions:
Radial Component (\(d_r\)): The distance in the XZ plane from the central axis to the cylinder's curved surface: \[d_r = \sqrt{x^2 + z^2} - r = \text{length}(p.xz) - r\]
Axial Component (\(d_y\)): The distance along the Y-axis from the center to the flat end caps: \[d_y = |y| - h\]
These components form a 2D offset vector: \[\vec{d} = (d_r, d_y) = (\text{length}(p.xz) - r, |p.y| - h)\]
The signed distance is derived by considering whether the evaluation point is inside or outside the cylinder boundaries:
- Outside Distance: \(\text{length}(\max(\vec{d}, 0.0))\) measures the Euclidean distance to the nearest edge or face when outside the shape along one or both axes.
- Inside Distance: \(\min(\max(d_r, d_y), 0.0)\) measures the negative distance to the closest boundary when the point is inside the shape.
Combining both terms yields the complete signed distance: \[\text{SDF}(p) = \min(\max(d_r, d_y), 0.0) + \text{length}(\max(\vec{d}, 0.0))\]
GLSL Implementation (Axis-Aligned)
The following GLSL function computes the exact Euclidean signed distance for a finite cylinder centered at the origin and oriented along the Y-axis:
float sdCylinder(vec3 p, float h, float r) {
vec2 d = abs(vec2(length(p.xz), p.y)) - vec2(r, h);
return min(max(d.x, d.y), 0.0) + length(max(d, 0.0));
}p: The sample position in 3D space (vec3).h: Half of the total height of the cylinder (float).r: The radius of the cylinder (float).
GLSL Implementation (Arbitrary Orientation)
To define a cylinder with arbitrary orientation and position between
two endpoints a and b, the coordinates are
projected along the segment axis:
float sdCylinder(vec3 p, vec3 a, vec3 b, float r) {
vec3 ba = b - a;
vec3 pa = p - a;
float baba = dot(ba, ba);
float paba = dot(pa, ba);
float x = length(pa * baba - ba * paba) - r * baba;
float y = abs(paba - baba * 0.5) - baba * 0.5;
float x2 = x * x;
float y2 = y * y * baba;
float d = (max(x, y) < 0.0) ? -min(x2, y2) : (((x > 0.0) ? x2 : 0.0) + ((y > 0.0) ? y2 : 0.0));
return sign(d) * sqrt(abs(d)) / baba;
}This formulation avoids explicit matrix transformations by projecting \(p\) directly onto the line segment \(\vec{ab}\), correctly returning positive values outside the volume, negative values inside, and zero on the boundary.