Geometry
Everything so far has lived in flat space — Euclidean planes and boxes. This section moves into curved and higher-dimensional geometry, where the pullback approach becomes especially powerful.
Drawing a geodesic in the hyperbolic plane, tiling
A note on difficulty: the shaders in this section are more involved than the earlier ones. The 2D examples (stereographic projection, hyperbolic tilings) are comparable to what came before, but the 3D hyperbolic honeycombs and the Hopf fibration use more elaborate distance functions and raymarching setups. You don't need to follow every implementation detail to use them — the "edit the function at the top" pattern still applies — but the explanations go deeper into the geometry to show what these techniques can do.
We start with stereographic projection — the bridge between planes and spheres — then move to the hyperbolic plane and its tilings, circle packings, hyperbolic 3-space, and the Hopf fibration.
3.0.1Stereographic Projection
This shader shows a curve drawn simultaneously in the plane and on the sphere, connected by stereographic projection.
The sphere sits at the origin with its south pole on the
The curve on the sphere uses the same
The scene includes a grid on the ground plane, soft shadow and ambient occlusion from the sphere onto the plane, and Phong shading with Fresnel on the sphere.
3.0.2Hyperbolic Geometry
The hyperbolic plane is a natural first stop: the geometry is curved, parallel lines diverge, triangles have angle sums less than
All the hyperbolic shaders work in the upper half-plane model internally, where #define.
The models are related by conformal (or projective, for Klein) maps, and the shader applies these at the pixel level: convert screen coordinates to UHP coordinates, do all the geometry there, convert back.
Models of the hyperbolic plane
The Poincaré disk maps
The upper half-plane is the most natural for computation. Geodesics are semicircles centered on the real axis (or vertical rays). Reflections across geodesics are circle inversions or Euclidean reflections, both easy to compute.
The Klein disk maps
The band model maps
Hyperbolic Geometry Explorer
The explorer is a graphing calculator for the hyperbolic plane. You specify points, geodesics, and horocycles in UHP coordinates, and they're drawn in whichever model you choose.
Points are specified as
Geodesics are specified by two UHP points.
The shader finds the unique geodesic through them — either a vertical line (if the points share an
Horocycles are circles tangent to the boundary at infinity.
In UHP, a horocycle at a finite point
Each object type has per-object colors and sizes (radii, thicknesses), so you can distinguish different elements visually.
Triangle Tilings
A triangle group
The fundamental triangle is built in UHP with a specific layout:
Side
: the vertical line𝑎 . This is the simplest geodesic to reflect across — just negate theR e ( 𝑧 ) = − c o s ( 𝜋 / 𝑃 ) -offset.𝑥 Side
: the unit semicircle𝑏 . Reflection is circle inversion:| 𝑧 | = 1 .𝑧 ↦ 1 / ¯ 𝑧 Side
: a computed semicircle whose center and radius come from the hyperbolic law of cosines applied to the triangle's angles.𝑐
The three vertices sit at the intersections of these curves, computed from the circle equations.
To render, every pixel is folded into the fundamental triangle by iterated reflection: check if the point is on the wrong side of each mirror, and if so, reflect it. Repeat until stable (at most ~100 iterations, though convergence is usually fast). The number of reflections gives the parity — even means orientation-preserving, odd means orientation-reversing — which provides a natural two-coloring.
Edges and vertices are drawn by computing hyperbolic distance from the folded point to the triangle's sides and vertices.
Wythoff Tilings
The triangle tiling shows the underlying
Different positions of
at a vertex: the orbit traces out a regular polygon tiling. For the𝐺 group, placing( 2 , 3 , 7 ) at the vertex with angle𝐺 gives the𝜋 / 3 tiling (regular heptagons, three meeting at each vertex).{ 7 , 3 } on a mirror edge: placing it at the point equidistant from the two opposite mirrors gives a uniform tiling — all edges have the same hyperbolic length.𝐺 at the incenter (equidistant from all three mirrors): the omnitruncated tiling, with three different polygon types meeting at every vertex.𝐺
The shader computes
The key insight for rendering is that after folding a pixel into the fundamental triangle, the Wythoff edges are just geodesic segments from
3.0.3Apollonian Circle Packing
Leaving hyperbolic tilings, we turn to a classical construction from inversive geometry. An Apollonian gasket starts from four mutually tangent circles and fills in every interstice: given any three mutually tangent circles, there are exactly two circles tangent to all three, and the gasket is the limit of inserting every such circle.
The starting point is Descartes' circle theorem.
If four circles are mutually tangent with curvatures
This is a quadratic in
The two roots correspond to the two circles tangent to a given triple — the small one filling the interstice (
The shader asks you to specify three curvatures K1, K2, K3 and a sign choice INNER.
It computes
To render the gasket, the shader uses iterated circle inversions. Given the four initial circles, their six tangent points determine four dual circles — each dual circle passes through the three tangent points that don't involve a given initial circle. Inverting through a dual circle swaps the two initial circles on either side of it, acting as a "reflection" in the packing.
The algorithm for each pixel:
Check if the pixel is inside any of the four initial circles — if so, color it and stop.
Otherwise, find which dual circle contains the pixel and invert through it. This maps the pixel into a smaller copy of the packing.
Repeat until the pixel lands inside a circle or the iteration limit is reached.
The number of inversions gives a "depth" that controls the color fade — deeper circles wash toward white. Circle boundaries are drawn with a thickness that scales with the cumulative Jacobian of the inversions, so borders have consistent pixel width at every depth.
Some configurations to try:
(1, 1, 1)withINNER = false— the classical Apollonian gasket, four equal-ish circles(2, 2, 3)— an asymmetric packing(0, 0, 1)— two parallel lines and a circle between them, giving a strip packing(0, 1, 1)— one line and two circles, a half-plane packing
This shader was written by Summer Haag (University of Colorado Boulder), a graduate student at the IHP trimester program.
3.0.4Hyperbolic 3-Space
Everything above lives in the hyperbolic plane.
Now we go up a dimension to
The model we use is the hyperboloid model: embed
with the induced metric from the Minkowski inner product
This is the 3D analog of the upper half-plane model — but better suited to computation because every isometry of
Distance:
𝑑 ( 𝑝 , 𝑞 ) = a r c c o s h ( − ⟨ 𝑝 , 𝑞 ⟩ ) Geodesics:
where𝛾 ( 𝑡 ) = c o s h ( 𝑡 ) 𝑐 + s i n h ( 𝑡 ) 𝑣 and𝑐 ∈ ℍ 3 is a unit spacelike tangent at𝑣 𝑐 Reflection through a hyperplane with unit spacelike normal
:𝑛 𝑝 ↦ 𝑝 − 2 ⟨ 𝑝 , 𝑛 ⟩ 𝑛
To specify a point, you give spatial coordinates
Points and Geodesics
This first shader is a simple explorer: place balls at points in
Raymarching works just like in Euclidean space, except the ray is a geodesic:
Try moving the points farther from the origin and watch how the geodesic lines curve — or rather, how they stay straight while the space curves around them.
Ideal Tetrahedral Honeycomb
An ideal tetrahedron in
The tiling algorithm is the same fold-into-fundamental-domain technique from the 2D tilings, now in one higher dimension.
The fundamental tetrahedron has 4 face mirrors, each a totally geodesic hyperplane in bounce function iterates: check which face half-space
After folding, the edge SDF computes distance to the 6 edges of the tetrahedron using
Dodecahedral Honeycomb
A regular dodecahedron has pentagonal faces with interior angles of
The same bounce algorithm applies, but now with 12 face mirrors instead of 4.
Icosahedral symmetry compresses this: abs(p.xyz) handles sign flips, and a
After folding, the SDF tests distance to the 20 vertices and 30 edges of the fundamental dodecahedron.
The vertices split into two icosahedral orbits (8 "cubic" + 12 "golden"), reduced to 4 dot products by abs and cyclic symmetry.
The edges split into 6 axis-aligned + 24 golden, packed into vec4 batches — four sign combinations at once — computing
Edges are colored by depth: the
3.0.5Hopf Fibration
The Hopf fibration
This shader raymarches those fibers in
The SDF via the bundle structure
The key formula is the distance from a point
This works by lifting everything to
We just compose three maps — inverse stereographic projection, the Hopf map, and a dot product — and get an SDF directly from the bundle geometry.
Intrinsic vs. extrinsic thickness
The INTRINSIC toggle changes where the tube radius is subtracted.
In extrinsic mode (INTRINSIC 0), the radius is subtracted after multiplying by the conformal factor:
dFiber = conformal * ang - TUBE_RADIUS
This gives fibers of uniform Euclidean thickness in
In intrinsic mode (INTRINSIC 1), the radius is subtracted on
dFiber = conformal * (ang - TUBE_RADIUS)
Now fibers have uniform thickness in the round metric on
Base point distributions
The shader draws the fiber over each point returned by basePoint(i).
Two distributions are provided:
The latitude rings layout places points along parallels of
The Fibonacci spiral distributes points approximately uniformly over
Fibers are colored by their base point: hue encodes longitude on
3.0.6Hopf Preimage of Curves
The Hopf fibration shader draws individual fibers — the preimage of a finite set of points on
The chain of maps is:
A curve
The user interface is as simple as it gets: write float curve(vec2 w) returning curve per sample, which costs almost nothing compared to the geometric chain (
The SDF via chained conformal factors
The distance from
Each factor has a geometric meaning.
The ratio
This is the same gradient-correction idea from the 2D level set shaders, but now it's being composed through three conformal maps.
Raymarching a thin shell
The preimage is a surface (codimension 1), not a solid region, so it has to be rendered as a thin shell: abs(d) - thickness is notoriously fragile — the SDF has a crease at the zero-set, and sphere tracing steps right over it.
The shader avoids this by stepping with the signed distance to the zero-set (not the shell). This makes the ray decelerate as it approaches the surface from either side. As a safety net, it tracks sign changes between consecutive samples: if the signed distance flips, the ray has crossed the zero-set, and the shader bisects to find the crossing. This gives clean surfaces even for thin shells.
Example curves
The shader includes several curves to try:
Circle
— the equator of| 𝑤 | 2 = 1 , whose preimage is the Clifford torus.𝑆 2 Line
— a great circle through the poles, giving a torus that passes through infinity (appears as an unbounded surface after stereographic projection).𝑢 = 0 Lemniscate
— a figure-eight on( 𝑢 2 + 𝑣 2 ) 2 = 2 ( 𝑢 2 − 𝑣 2 ) , producing a pinched surface.𝑆 2 Elliptic curve
— the preimage inherits the topology of the curve's compactification on𝑣 2 = 𝑢 3 − 𝑢 .𝑆 2 Trefoil
— a three-lobed curve whose preimage is a knotted surface.R e ( 𝑧 3 ) = 1 / 2 Two concentric circles — two Clifford tori at different radii, nested inside each other.