Fractals > Signed Distance Function (SDF) Fractals
Lots of things around SDF functions and fractals, there are the key things we'll talk about about here:
1. What are SDF Functions?
2. Simple SDF (WebGL/GLSF) Example (Sphere)
3. 'Flat' Fractal (2D)
4. 3D Shapes (Solid Sphere, Hollow-Sphere, Shell, ...)
5. Adding function (create more complex shapes)
6. Multiple shapes (hard way vs smart way)
7. Multiple operations to build fractals
8. Scene full of fractals
9. Limitations, challenges (soft fractals without details)
What are 'Signed Distance Functions' (SDFs)?
A signed distance function is a mathematical concept used in computer graphics to describe the distance from a point in space to the closest surface of an object, with a positive or negative sign indicating whether the point is inside or outside the object.
Takes a 'point' (usually 3d point) and returns a single number (usually a float).
For example, in 3D sphere, the signed distance function at a point (x, y, z) would be the radius of the sphere minus the distance from (x, y, z) to the center if the point is inside the sphere, and negative if it's outside.
This function is valuable in rendering techniques like ray-marching, where it helps determine intersections with objects in a scene efficiently.
SDF Examples (Spheres)
The SDF function requires a single parameter for the position that it's calculating the distance so the surface - other arguments can be passed as well - such as the radius, ..
SDF WebGL/GLSL for 'Circle'
Extending the Example (Sphere to Mandelbrot Set Fractal)
SDF for a Sphere (3D)
Bit of Light
Without lighting the 3D sphere looks exactly the same as the 2D sphere - you can't see depth or changes in the surface - but we can write a simple function to calcualte the surface normal from the point.
To calculate the surface normal using the SDF function, you can approximate it by taking the gradient of the SDF at a given point.
The fragment shader code that calculates the normal for the surface using the SDF function and then use it to calculate lighting.
Combining Shapes (Unions)
This is a crucial component - as you can easily add or subtract simple SDF functions to create new shapes. This is important for 'fractals' - as you'll see, it means you can combine shapes to make smaller sub-shapes.
For instance, it is possible to combine two distance fields using a simple `minimum(a,b)`` operator. As an example we could draw the union of two spheres.
This would give us two spheres with unit radius, one centered at origo, and another at `(2,0,0)`. The same way it is possible to calculate the intersection of two objects, by taking the maximum value of the fields.
Finally, if you are using signed distance functions, it is possible to subtract one shape from another by inverting one of the fields, and calculating the intersection (i.e. taking `max(A, -B)`).
Take the concept of combining objects further - apply local transforms to create amazing effects (e.g., stretch, rotate, skew and then add them together - you can also do this in a 'loop'.
Construct a 'rayCast' function which can combine multiple different sdf functions in one place (transform, add, subtract, ...).
Other SDF Shapes
Cube-Sphere Union
If you use 'max' - to subtract the shapes, you'll get this:
Cube Hole (Cube Cut Cube)
Subtract a cube from a cube to hollow out the inside - initial work for later on when we'll do this recursively to build a fractal shape.
This example, the values are hard coded - for easy reading - but we can calculate the offsets and sizes later on to 'chizzel' away at a shape to make it 'fractal'.
Cube in Cube (Adding and Cutting Iteratively)
Iteratively add and cut to create shapes - for example, the following adds cube and cuts out the centre.
You can take the concept further by mixing on rotation - for each iteration add a small 'rotation'.
Complexity (Lots of Shapes) - 'mod'
This is all nice, but even if you can create interesting structures, there are some limitations. The above method works fine, but scales very badly when the number of distance fields to be combined increases. Creating a scene with 1000 spheres by finding the minimum of the 1000 fields would already become too slow for real-time purposes. In fact ordinary ray tracing scales much better – the use of spatial acceleration structures makes it possible for ordinary ray tracers to draw scenes with millions of objects, something that is far from possible using the “find minimum of all object fields” distance field approach sketched above.
But fractals are all about detail, and endless complexity, so how do we proceed?
It turns out that there are some tricks, that makes it possible to add complexity in ways that scales much better.
First, it is possible to reuse (or instance) objects using e.g. the modulo-operator. Take a look at the following DE:
Example - Rotating and Repeating Cube-Spheres
Recursive Tetrahedron Fractal
Let's bring it all together - combining objects to construct fractals.
A tetrahedron may be described as a polyhedron with vertices (1,1,1),(-1,-1,1),(1,-1,-1),(-1,1,-1). Now, for each point in space, lets us take the vertex closest to it, and scale the system by a factor of 2.0 using this vertex as center, and then finally return the distance to the point where we end, after having repeated this operation. Here is the code:
WebGL version of GLSL is limited e.g.
Even though we do not have an infinite number of objects, like the mod - example above, the number of objects grow exponentially as we crank up the number of iterations. In fact, the number of objects is equal to 4^Iterations. Just ten iterations will result in more than a million objects - something that is easily doable on a GPU in real-time!
SDF Folding Space
When constructing fractals by iteratively combining shapes, there is a smart trick that takes advantage of 'mirroring' (or folding). This clever trick utilizes the symmetries of the fractal. For instance, if you look at the tetrahedron fractal example, you'll notice it's symetrical across certain axis - so we can use that to optimize the implementation (use folds).
Instead of scaling about the nearest vertex, we mirror points in the symmetry plane of the tetrahedron, to make sure that we arrive at the same octant of the tetrahedron - and then always scale from the vertex it contains.
Here is the code - it produces the same result - but uses folds instead - so it's more compact and efficient.
The folding operations is an interesting aspect of fractals with sdf, and builds up on 'mod' and 'combining' aspects - however, the really interesting stuff with folding happens when we throw rotations into the process. Imagine kaleidoscopic fractals - which produces fractals like the Menger Sponge.
Mixing in rotations, translations and scaling into the fold.
Lighting and SDF
Lighting helps make the fractals more beautiful - as it highlights the detail. Of course, there are more than one ways to light a scene (not just directional lighting).
Phong shading
Phong shading is a technique used to simulate the interaction between the light sources and surfaces, considering diffuse, specular, and ambient reflections, resulting in smooth shading with highlights and shadows.
Ambient Occlusion
Besides the ambient, diffuse, and specular light from Phong-shading, one thing that really improves the quality and depth illusion of a 3D scenes is ambient occlusion.
One approach is to count the number of ray steps as a very rough measure of how occluded the geometry.
Another approach is to sample the Distance Estimator at points along the normal of the surface and use this information to put together a measure for the Ambient Occlusion. This is a more intuitive method, but comes with some other shortcomings – i.e. new parameters are needed to control the distance between the samplings and their relative weights with no obvious default settings.
Glow
Glow can be added simply by mixing in a color based on the number of ray steps taken (points close to the fractal will use more ray steps, even if they miss the fractal, so pixels close to the object will glow).
Fog
Fog is also great for adding to the depth perception. Simply blend in the background color based on the distance from the camera.
Hard shadows
Hard shadows are also straight forward – check if the ray from the surface point to the light source is occluded. Hard shadows have crisply defined, sharp edges.
Soft shadows
Soft shadows are the softened shadows (feathered around the edge).
Reflections
Reflections are pretty much the same – reflect the camera ray in the surface normal, and mix in the color of whatever the reflected ray hits.
Colors
Colors can be use to visualize important information (edges or detail around a fractal).
Orbit Traps
Orbit traps is a popular way to color fractals. This method keeps track of how close the orbit comes to a chosen geometric object. Typical traps include keeping track of the minimum distance to the coordinate system center, or to simple geometric shapes like planes, lines, or spheres. In Fragmentarium, many of the systems use a 4-component vector to keep track of the minimum distance to the three x=0, y=0, and z=0 planes and to the distance from origo. These are mapped to color through the X,Y,Z, and R parameters in the ‘Coloring’ tab.
Iteration Count
The iteration count is the number of iterations it takes before the orbit diverges (becomes larger than the escape radius). Since this is an integer number it is prone to banding, which is discussed later in this post. One way to avoid this is by using a smooth fractional iteration count:
Conditional Path Coloring
Some fractals use conditional logic inside the iteration loops (sometimes disguised as `abs` operator to avoid if statements). The Mandelbox is a good example: the sphere fold performs different actions depending on whether the length of the iterated point is smaller or larger than a set threshold. This makes it possible to keep track of a color variable, which is updated depending on the path taken.
Nutshell
Fusing signed distance functions (SDFs) with ray-tracing opens the door to a quick and powerful way to create complex interactive fractals. Combined with the processing power of the GPU; which can be executed in JavaScript using various API (like WebGL/GLSL or WebGPU/WGSL); there are no limits to what you can create!
Visitor:
Copyright (c) 2002-2025 xbdev.net - All rights reserved.
Designated articles, tutorials and software are the property of their respective owners.