Generating Fractals with Blender and Animation-Nodes | by 5agado | Towards Data Science

What: this is a collection of tutorials, examples and resources for generating fractals visuals using Blender. I’ll cover topics such as recursion, n-flakes, L-systems, Mandelbrot/Julia sets and derivations.
Why: if you are fascinated by fractals patterns, interested in procedural generation in Blender and want to get a better understanding and hands-on experience of animation-nodes and shader nodes.
Who: all content here is based on Blender 2.8.2 and animation-nodes v2.1. I also relied on a few short code snippets (Python ≥ 3.6).
“beautifully intricate, yet so simple”
Fractals are shapes with fractal dimensions. This derives from the way we measure them as they scale. Theoretical fractals are infinitely self-similar across different scales.
For our setup, we are not concerned with pure theoretical fractals, as those are not achievable in Blender, unless under very specific circumstances. We mostly care about fine structure (details at different scales) and a natural appearance of self-similarity (made up of apparent smaller copies of itself).
Additional suggested resources to learn more about fractals:
[YouTube] 3Blue1Brown on Fractals
[Book] The Fractal Geometry of Nature by Benoît B. Mandelbrot
[Book] Fractals: A Very Short Introduction by Kenneth Falconer
[Book] The Algorithmic Beauty of Plants by Przemysław Prusinkiewicz
An n-flake (or polyflake) is a fractal constructed by replacing an initial shape with multiple scaled copies of itself, generally placed in correspondence to vertices and center. The process is then repeated recursively for each new shape.
This is a good starting point for our exploration of fractals, as it allows us to cover recursion and other important concepts via a simple animation-nodes setup.
Which brings us to our main issue: animation-nodes doesn’t (currently) support pure recursion. We need some workarounds. One option is always to rely on pure Python scripting, as I explained in a previous entry, but I wanted the focus of this piece to be more on animation-nodes, so what we can do is to approximate recursion via iterations and loop queues.
The idea is to rely on the Loop Input Reassign option to keep a queue of results which we can then be processed in the next iteration.
Let’s consider the n-flake case for regular polygons. Given a number of segments n(the number of sides of the polygon), a radius r and center c, we compute the points of the n-polygon centered in c with radius r. For each new computed point p we repeat this procedure (that is, finding the polygon centered in p), but resizing the radius r by some predefined factor.
All the main logic resides in the following loop, which takes care of computing the polygon points and new radius, and reassigning it such that the next iteration can process them. It is important to understand this part: during the first iteration the loop processes whatever has been given to the Invoke Subprogram node, while for all successive iterations the loop will process the values that it updated in the previous iteration. Each time these are cleaned-up, and that’s why we maintain two queues (centers_queue and all_centers), the former is the centers we haven’t yet processed, the latter is a collection of all centers computed so far, which will be used as output to create our list of splines.
The two subprograms are both Python script. These are examples where I find coding more concise and not necessary to be translated to pure nodes.
Compute the points of a regular polygon:
points = []# compute regular polygon points for each given centerfor center in centers:    angle = 2*pi/segments  # angle in radians    for i in range(segments):        x = center[0] + radius*cos(angle*i)        y = center[1] + radius*sin(angle*i)        z = center[2]  # constant z values        points.append((x, y, z))    #points.append(points[-segments])  #  close the loop
Get scale factor:
from math import cos, pi# compute scale factor for any n-flake# https://en.wikipedia.org/wiki/N-flakecumulative = 0for i in range(0, segments//4):    cumulative += cos(2*pi*(i+1) / segments)scale_factor = 1 / (2*(1 + cumulative))
The additional loop setup is to split points such that each polygon is converted to a separate spline.
We can easily adapt this setup to work with regular solids. This time, instead of computing the shape vertices ourselves, we just rely on an object already present in the scene, get its vertices and recursively transform them. We leverage matrix properties and the Compose Matrix node to minimize the amount of work or nodes required. Here the setup
Out matrix_queue is initialized with an identity transformation matrix (no translation, no rotation and unit scale) while our transformation list is our target/input object vertices locations composed with an arbitrary scale factor, which is equivalent to say “shift everything there, and scale by the given factor” when used to transform another matrix.Here the adapted recursive part which executes the transformation loop and reassign operations.
Sample results for platonic solids (between four and five iterations).
One can then update the original translation matrix, such that the new solids are not placed exactly on predecessor vertices, but instead on any point of the line passing through the predecessor center and new vertex, as illustrated in the side animation.
While previously the translation matrices were purely the vertices location, we are now doing some vector math to shift such points from the original location. If we subtract from them the object center, we obtain the direction vector of interest. We can then divide this by arbitrary values to place the new recursive solids accordingly.
If we instantiate only objects of the last iteration we obtain proper platonic solid fractals. Well known examples are the Sierpinski tetrahedron and the Menger Sponge.
arbitrary/random change of polyflake type across recursive steps
use of displacement maps to simulate additional recursive steps without the need of creating the finer-details additional geometry
volumetric and pure shader nodes setup
An L-system (or Lindenmayer system) is a grammar; something that specifies how to generate textual strings via a set of rules, an alphabet and initial axioms. It is accompanied by a translation mechanism to transform such strings to geometry.
We are interested in L-systems because they can be used to generate self-similar fractals and also because we kinda get them for free out-of-the-box with animation-nodes via LSystem node. Here an example setup that instantiates the results to object.
The one on the side is a fractal-plat configuration defined by axiom X (the initial state) and two rules X=F[+X][-X]FX and F=FF . The node takes care of evolving the system for the specified number of generations, which nicely enough can be fractional, allowing for smooth transitions. It also converts textual results to a mesh. For this reason you will have to follow the specified convention for axioms and alphabet usage.
Another important parameter to consider is Symbol Limit in the advanced node settings, which specifies the maximum length of the generated string. The system will throw an error if exceeding this limit.
dragon curve - axiom: FX, rules: (X=X+YF, Y=-FX-Y), angle: 90fractal plant - axiom: F, rules: (F=FF-[-F+F+F]+[+F-F-F]), angle: 22.5sierpinski triangle - axiom: F-F1-F1, rules: (X=X+YF, Y=-FX-Y), angle: 120
3D L-systems (e.g. azimuth + inclination) and stochastic L-systems. (see Python code)
Step size rule, proportional to growth such that view can be kept constant while increasing generations
As the father of fractals, it is of no surprise that the most common and frequently displayed fractals bear his name. Together with Julia sets, the Mandelbrot set provide theoretical foundation for a phenomenon that is again defined by simple rules, but able to generate an infinite picture of beauty and intricacy.
We can think of these sets as “iteration of functions expressed in coordinate form”, in which we are dealing with the plane of complex numbers. This entry by Jeremy Behreandt provides an impressively detailed introduction to complex numbers, Mandelbrot Set and much more, and all in Blender’s Python API and Open Shading Language (OSL).
We can achieve similar results purely in Blender nodes (no scripting), but as usual we are limited by the missing iterative/recursive capabilities of the setup. The basic idea is to move around the plane of complex number as dictated by the formula
coloring each point based on the speed to which it converges to infinite. Here z is our starting point, and c can be an arbitrarily chosen complex number. The formula can be rewritten in pure cartesian-plane coordinates form as
where we now have (x,y) as our point, and a and b as our control values (the real and imaginary part of our previous c).Here the translation of the formula in shader nodes setup
The iterations_count part is used to keep track of valid iterations, the ones for which the point didn’t diverge. For practical purposes, this can be approximated by checking that the vector magnitude is less than two.
We can then plug in our texture coordinate, by separating x and y as illustrated. Reusing them for a and b gives us the Mandebrot set, or we can otherwise pass custom values to explore other sets and results.Now the ugly part: we have to approximate the function iterations via a very-non-elegant copy-and-paste mess, which for me ended up looking something like this. 100 iterations is a decent estimate to provide good results, the more you add the more fine details you get, but the setup can quickly become slow.
But at least we now have something to play with
The Mandelbulb is a projection in 3D of the Mandelbrot set. Again there is already an awesome tutorial out there by Jonas Dichelle explaining how to simulate a Mandelbulb in Blender nodes and Eevee. Given that the method relies on volumetrics, it’s worth referencing also this Blender Conference talk by Gleb Alexandrov about 3D Nebulae.
I have always been captivated by phenomena that exhibit complexity out of simple rules, and the ability to visualize them makes it all even more intriguing. Blender just makes it so easy, and animation-nodes is just one more addition to an already present set of amazing tools/utils, especially from a procedural point of view.
I admit that getting a couple of steps away from pure coding feels a bit frightening to me, like a visceral feeling I’m not doing the right thing, but there is no doubt about the efficiency one can achieve my using nodes, from an iterative and experimental point of view. I still believe some parts are better suited as separate scripts, and also that people should always build/understand basics first with code, as it provides a better framework for complexity breakdown and organizations as well as problem-solving generalization.
I am planning to explore more of similar topics, refining both a theoretical understanding and practical capabilities to reproduce them in a compelling way.
In particular these are the current top entries:
beyond 3 dimensions
cellular automata
morphogenesis
reaction diffusion
strange attractors
tessellation
epicycles
I welcome any suggestions, critiques and feedback.
You can see more of my “polished” graphics results on Instagram, read more technical/breakdown explanations on Twitter and try out my code on Github.