Skip to main content

capsuleSDF: Three-Dimensional Capsule Distance Field

Cylindrical Form with Rounded Endpoints

The capsuleSDF function computes the signed distance from any 3D point to a capsule shape. A capsule is essentially a cylinder with hemispherical caps, defined by two endpoint positions and a radius value.

Mathematical Foundation

The capsule SDF operates through line segment projection and distance calculation:

dcapsule=pabahrd_{\text{capsule}} = \|\vec{pa} - \vec{ba} \cdot h\| - r

where the projection parameter is computed as:

h=clamp(pabababa,0,1)h = \text{clamp}\left(\frac{\vec{pa} \cdot \vec{ba}}{\vec{ba} \cdot \vec{ba}}, 0, 1\right)

The vectors are defined as pa=pa\vec{pa} = p - a and ba=ba\vec{ba} = b - a, where aa and bb are the capsule endpoints.

Function Signature

ParameterTypeDescription
pvec3Sample point position
avec3First endpoint of capsule axis
bvec3Second endpoint of capsule axis
rfloatCapsule radius

Implementation Demonstrations

Live Editor
const fragment = () => {
      const pos = vec3(uv.x.mul(4).sub(2), uv.y.mul(4).sub(2), 0)
      const startPoint = vec3(-1, -0.5, 0)
      const endPoint = vec3(1, 0.5, 0)
      const radius = 0.3
      const dist = capsuleSDF(pos, startPoint, endPoint, radius)
      const inside = dist.step(0)
      const edge = float(0.02).smoothstep(0, dist.abs())
      const color = inside.mul(0.8).add(edge.mul(0.4))
      return vec4(vec3(color), 1)
}
Live Editor
const fragment = () => {
      const up = vec3(0, 1, 0)
      const eps = vec3(0.01, 0, 0)
      const eye = rotate3dY(iTime).mul(vec3(5))
      const args = [vec3(-1, -1, 0), vec3(1, 1, 0), 0.3]
      const march = Fn(([eye, dir]: [Vec3, Vec3]) => {
              const p = eye.toVar()
              const d = capsuleSDF(p, ...args).toVar()
              Loop(16, ({ i }) => {
                      If(d.lessThanEqual(eps.x), () => {
                              const e = vec3(0.001, 0, 0)
                              const dx = capsuleSDF(p.add(eps.xyy), ...args).sub(d)
                              const dy = capsuleSDF(p.add(eps.yxy), ...args).sub(d)
                              const dz = capsuleSDF(p.add(eps.yyx), ...args).sub(d)
                              return vec4(vec3(dx, dy, dz).normalize().mul(0.5).add(0.5), 1)
                      })
                      p.addAssign(d.mul(dir))
                      d.assign(capsuleSDF(p, ...args))
              })
              return vec4(0.2, 0.2, 0.2, 1)
      })
      const z = eye.negate().normalize()
      const x = z.cross(up)
      const y = x.cross(z)
      const scr = vec3(uv.sub(0.5), 2)
      const dir = mat3(x, y, z).mul(scr).normalize()
      return march(eye, dir)
}