I created this work as a final project for my course Modern Computational Methods in Physics 1 at ELTE in 2021. I got the original idea from the Facebook group “Aesthetic Function Graphposting” and wanted to have a shot with it myself. In the project I implemented the “classic” Perlin noise generation scheme and created a fractal noise map by stacking several Perlin noise maps on top of each other. Then, I treated the fractal map as a discrete force/displacement field and moved random particles around it by applying this well-defined force/displacement on each particle. Dissecting the Perlin noise generation algorithm, I also created a step-by-step visualizations of the noise generation process for others to understand its workings.
Perlin noise is a type of gradient noise that was introduced and published by Ken Perlin in his paper “An image synthesizer” in 1985[1]. Perlin further improved the noise function in 2002, where he introduced the “simplex noise”[2]. Perlin noise significantly impacted computer-generated imagery, graphics and visualization, and it is still actively used in procedural texture and terrain generation today (see Minecraft as the most famous example of this).
In this project, I reproduced the theoretical basics of the original “classic” algorithm for 2D Perlin noise generation that I’ll present in the following sections. As an exciting addendum, I derived a gradient field from the created noise map, interpreted it as a force field, and simulated the movement of particles on it. Finally, I visualized their trajectories in a creative/artistic manner. Originally, I implemented the Perlin noise generation and particle tracing using C++17
, while the visualizations and data analysis pipeline were made entirely in Python 3.9
.
Perlin’s papers (from 1985 and 2002) and his code implementation were quite “laconic”. His uncommented and concise example code only demonstrates the principle, while his papers are also short-spoken. While it can be definitely explained more thoroughly in detail, I think a visual approach to Perlin noise generation is a much better way to understand every detail of the algorithm. In this section, I’ll present the noise generation process step-by-step, using simple and easy-to-understand visualizations.
The base structure of the classic Perlin noise consists of two overlapping grids of different resolutions. The two grids span between the same boundaries, while their edges overlap on all sides. An example configuration can be seen in Fig. 1., where I plotted both the main and the sub grid. The vertices of the main grid are marked with larger, white circles, while the nodes of the sub grid are marked with smaller, brownish-yellowy dots.
The main the and sub grid respectively define the frequency and the resolution of the simulated Perlin noise map. By increasing the number of vertices of the main grid, we are increasing its frequency, and by increasing the number of vertices in the sub grid, we are increasing its resolution. Larger freuquencies will result in a noise map that covers a larger area, like looking at a map from a higher altitude. On the other hand, a noise map with larger resolution will simply have more detail, but it will cover the same area as a noise map with lower resolution, but the same frequency.
The second step in the Perlin noise generation is to create a gradient field. The vertices of this field will coincide with the main grid points. In this step we have to place a gradient vector with randomly sampled arguments into each main grid vertices. They will define our main gradient field. This component is visualized in Fig. 2., where the gradient vectors are marked with red arrows. In my project the arguments of each gradient vectors were sampled from the uniform distribution.
In the third step, first we calculate the distance vectors between the sub grid points and all corners of the main cell they reside in. In 2D this would mean we’re assigning 4 distance vectors to every sub grid points. Then, the dot product of all distance vectors and their corresponding cell corners are calculated. This will leave us with 4 different “dot product maps”, each of them corresponding to either one of the cell corners (upper left, upper right, etc.). On Fig. 3. the dot product field corresponding to the upper right corners is shown.
After we obtained all the dot product maps, we need to smooth these maps out to get the final Perlin noise field. This interpolation can be carried out by several different methods and one can use whatever method seems to be the most reasonable for them in any specific case. However, in the usual Perlin noise implementations, the Smoothstep interpolation method is used. and I’ve implemented its 5th order variant for my project too. The 5th order Smoothstep function that interpolates between two values is the following:
\[i = \left( d_{1} - d_{0} \right) \cdot \left( \left( w \cdot \left( w \cdot 6.0 - 15.0 \right) + 10.0 \right) \cdot w^{3} \right) + d_{0}\]Since every sub grid vertex has exactly four dot grid values assigned to it, the interpolation has to be carried out three times. Two times between two pairs of the four dot field values, then third between the two results of these two interpolations. The final value will be assigned to the corresponding sub grid point at the end of the process. The map created by this process is what we call as Perlin noise.
In Fig. 4. we can see an example image of the Perlin noise, which was directly obtained from the interpolation of the four dot fields we calculated. By that, the Perlin noise generation section is over.
A common method employed in terrain/texture generation is to stack several noise maps on top of each other, each with a different frequency and amplitude, but the same resolution. This process results in a very detailed noise map, referred to fractal noise. Usually, several terms are introduced by various implementations here, like octaves, persistence, lacunarity, etc.
The given noise map field unambiguously defines a unique gradient field that can be interpreted as a force field in physics. In this field, particles can be moved by the forces in this field, just like in a real physical simulation. We can obtain this field directly from the Perlin noise map itself. In this case every sub grid point will have a corresponding gradient vector defined by the noise map. This field obtained from the noise map seen on Fig. 4. can be seen on Fig. 5.
Particles driven in this field van be further analyzed. The goal of this project was to obtain and visualize these particle trajectories in a creative manner. Results can be seen on Fig. 6., Fig. 7. and Fig. 8.
The figures shown here were obtained from a simulation with a main grid size of $3 \times 6$ and sub grid size of $400 \times 400$. During the particle tracing phase, $5000$ particles were simulated for $500$ steps in periodic boundary conditions. Their maximum velocity was limited to $0.1$ in internal units.