You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
thebookofshaders/12/README.md

179 lines
11 KiB
Markdown

9 years ago
![](dragonfly.jpg)
9 years ago
## Celluar Noise
9 years ago
In 1996, sixteen years after Perlin's Noise and five years before his Simplex Noise, [Steven Worley wrote a paper call “A Cellular Texture Basis Function”](http://www.rhythmiccanvas.com/research/papers/worley.pdf). In it he describes a procedural texturing technique now extensively use by the graphics community.
9 years ago
To understand the principles behind it we need to start thinking in terms iterations.
9 years ago
### A distance field for some points
Let's say we want to make a distance field of 4 points. What we need to do? in a nutshell, **for each pixel we want to calculate the distance to the closest point**. That means that we need to iterate through all the points and store the value to the most close one.
9 years ago
![](cell-00.png)
9 years ago
```glsl
float m_dist = 1.; // minimun distance
for (int i = 0; i < TOTAL_POINTS; i++) {
float dist = distance(st, points[i]);
m_dist = min(m_dist, dist);
}
```
9 years ago
To do that we can use a ```for``` loop to iterate through an array of points and keep track of the minimum distance using a [```min()```](../glossary/?search=min) function. Here a brief implementation of that:
9 years ago
<div class="codeAndCanvas" data="cellnoise-00.frag"></div>
9 years ago
Note in the above code, how one of the points is the mouse position. Play with it so you can get a more intuitive idea of how this code behaves. Then try this:
9 years ago
9 years ago
- How can you animate the rest of the points?
- After reading [the chapter about shapes](../07/), imagine interesting ways to use this distance field?
- What if you want to add more points to this distance field? What if we want to dynamically add/subtract points?
9 years ago
### Tiling and iterating
You probably notice that ```for``` loops and *arrays* are not so friendly in GLSL. Loops don't accept dynamic limits on their condition. Also iterating through a lot of instances reduce the performance of your shader significantly. So... We need to find another strategy.
9 years ago
![](cell-01.png)
9 years ago
One way to approach this problem is to divide the space in tiles. Not every pixel need to check every single points, right? They just need to check the points that are close to them. That's the main idea of [Steven Worley's paper](http://www.rhythmiccanvas.com/research/papers/worley.pdf). We already subdivide the space into cells in the chapters about: [patterns](../09/), [random](../10/) and [noise](../11/). Hopefully by now you are familiarize with this technique.
9 years ago
```glsl
// Scale
st *= 3.;
// Tile the space
vec2 i_st = floor(st);
vec2 f_st = fract(st);
```
So what's the plan? We will use the tile coordinates (stored in the integer coordinate, ```i_st```) to construct a random position of a point. The ```random2f``` function we will use receive a ```vec2``` and give us a ```vec2``` with a random position. So for each tile we will have one point in a random position.
9 years ago
9 years ago
```glsl
vec2 point = random2(i_st);
```
9 years ago
9 years ago
Each pixel inside that tile (stored in the float coordinate, ```f_st```) will check their distance to that random point.
9 years ago
```glsl
9 years ago
vec2 diff = point - f_st;
float dist = length(diff);
9 years ago
```
9 years ago
This will look like this:
9 years ago
<a href="../edit.html#12/cellnoise-01.frag"><img src="cellnoise.png" width="520px" height="200px"></img></a>
9 years ago
We still need to calculate the points around each pixel. Not just the one in the current tile. For that we need to **iterate** through the neighbor tiles. Not all of them. just the one immediately around it. That means from ```-1``` (left) to ```1``` (right) tile in ```x``` axis and ```-1``` (bottom) to ```1``` (top) in ```y``` axis. A 3x3 kernel of 9 tiles that we can iterate using a double ```for``` loop like this one:
9 years ago
```glsl
for (int y= -1; y <= 1; y++) {
for (int x= -1; x <= 1; x++) {
// Neighbor place in the grid
vec2 neighbor = vec2(float(x),float(y));
...
}
}
```
9 years ago
![](cell-02.png)
Now, we can predict the position of the points on each one of the neighbors in our double ```for``` loop by adding the neighbor tile offset to the current tile coordinate.
9 years ago
9 years ago
```glsl
9 years ago
...
// Random position from current + neighbor place in the grid
vec2 point = random2(i_st + neighbor);
...
```
9 years ago
9 years ago
The rest is all about calculating the distance to that point and store the closest one in a variable call ```m_dist``` (for minimal distance).
```glsl
...
vec2 diff = neighbor + point - f_st;
// Distance to the point
float dist = length(diff);
// Keep the closer distance
m_dist = min(m_dist, dist);
...
```
The above code is inspired by [this Inigo's Quilez article](http://www.iquilezles.org/www/articles/smoothvoronoi/smoothvoronoi.htm) where he said:
*"... it might be worth noting that there's a nice trick in this code above. Most implementations out there suffer from precision issues, because they generate their random points in "domain" space (like "world" or "object" space), which can be arbitrarily far from the origin. One can solve the issue moving all the code to higher precision data types, or by being a bit clever. My implementation does not generate the points in "domain" space, but in "cell" space: once the integer and fractional parts of the shading point are extracted and therefore the cell in which we are working identified, all we care about is what happens around this cell, meaning we can drop all the integer part of our coordinates away all together, saving many precision bits. In fact, in a regular voronoi implementation the integer parts of the point coordinates simply cancel out when the random per cell feature points are subtracted from the shading point. In the implementation above, we don't even let that cancelation happen, cause we are moving all the computations to "cell" space. This trick also allows one to handle the case where you want to voronoi-shade a whole planet - one could simply replace the input to be double precision, perform the floor() and fract() computations, and go floating point with the rest of the computations without paying the cost of changing the whole implementation to double precision. Of course, same trick applies to Perlin Noise patterns (but i've never seen it implemented nor documented anywhere)."*
9 years ago
Recaping: we subdivide the space in tiles; each pixel will calculate the distance to the point in their own tile and the other 8 tiles; store the closest distance. The resultant is distance field that looks like the following example:
<div class="codeAndCanvas" data="cellnoise-02.frag"></div>
Explore by:
- Scaling the space by different values.
- Can you think in other ways to animate the points?
- What if we want to compute an extra point with the mouse position?
- What other ways of constructing this distance field beside ```m_dist = min(m_dist, dist);```, can you imagine?
- What interesting patterns you can make with this distance field?
9 years ago
- Try to replicate this:
9 years ago
9 years ago
<a href="../edit.html#12/metaballs.frag"><img src="metaballs.gif" width="520px" height="200px"></img></a>
9 years ago
![](bubbles.jpg)
9 years ago
9 years ago
This algorithm can also be interpreted from the perspective of the points and not the pixels. In that case it can be describe as: each point grows until it finds the area of another point. Which mirrors some of the grow rules on nature. Living forms are shaped by this tension between an inner force to expand and grow limited by the forces
on their medium. The classic algorithm that simulate this behavior is named after [Georgy Voronoi](https://en.wikipedia.org/wiki/Georgy_Voronoy).
9 years ago
9 years ago
![](monokot_root.jpg)
9 years ago
### Voronoi Algorithm
9 years ago
Constructing voronoi diagrams from cellular noise is less hard that what it seams. We just need to *keep* some extra information about the precise point which is closer to the pixel. For that we are going to use a ```vec2``` call ```m_point```. By storing the position to the center of the closest point will be "keeping" and "unique" identifier of that point.
9 years ago
```glsl
...
if( dist < m_dist ) {
m_dist = dist;
m_point = point;
}
...
```
Note in the following code that we are not longer using ```min``` to calculate the closest distance but a regular ```if``` statement. Why? Because we actually want to do something every time a new closer point appears, store it position (lines 32 to 37).
9 years ago
9 years ago
<div class="codeAndCanvas" data="vorono-00.frag"></div>
Note how the color of the moving cell (hook to the mouse position) change color according it position. That's because the color is assigned using the value (position) of the closes point.
9 years ago
9 years ago
Like we did before now is time to scale this up, switching to [Steven Worley's paper approach](http://www.rhythmiccanvas.com/research/papers/worley.pdf). Try implementing it your self. You can use the help of the following example by clicking on it.
9 years ago
9 years ago
<a href="../edit.html#12/vorono-01.frag"><canvas id="custom" class="canvas" data-fragment-url="vorono-01.frag" width="520px" height="200px"></canvas></a>
Once you figurate out this algorithm think interesting and creative uses for it
9 years ago
9 years ago
![Accretion Disc Series - Clint Fulkerson](accretion.jpg)
9 years ago
### Improving Voronoi
In 2011, [Stefan Gustavson optimized Steven Worley's algorithm to GPU](http://webstaff.itn.liu.se/~stegu/GLSL-cellular/GLSL-cellular-notes.pdf) by only iterating trough a 2x2 matrix instead of 3x3. This reduce the work significantly but can create artifacts in form of discontinuities. Take a look to the following examples.
<div class="glslGallery" data="12/2d-cnoise-2x2,12/2d-cnoise-2x2x2,12/2d-cnoise,12/3d-cnoise"></div>
9 years ago
9 years ago
Later in 2012 [Inigo Quilez wrote an article on how to make precise voronoi borders](http://www.iquilezles.org/www/articles/voronoilines/voronoilines.htm).
9 years ago
<a href="../edit.html#12/2d-voronoi.frag"><img src="2d-voronoi.gif" width="520px" height="200px"></img></a>
9 years ago
Inigio's experiment on voronoi didn't stop there. In 2014 he wrote this nice article about what he call [voro-noise](http://www.iquilezles.org/www/articles/voronoise/voronoise.htm), and exploration between regular noise and voronoi. In his words:
9 years ago
*"Despite this similarity, the fact is that the way the grid is used in both patterns is different. Noise interpolates/averages random values (as in value noise) or gradients (as in gradient noise), while Voronoi computes the distance to the closest feature point. Now, smooth-bilinear interpolation and minimum evaluation are two very different operations, or... are they? Can they perhaps be combined in a more general metric? If that was so, then both Noise and Voronoi patterns could be seen as particular cases of a more general grid-based pattern generator?"*
9 years ago
9 years ago
<a href="../edit.html#12/2d-voronoise.frag"><canvas id="custom" class="canvas" data-fragment-url="2d-voronoise.frag" width="520px" height="200px"></canvas></a>
9 years ago
Now is up to you to observe and learn how to use and modify this technique for your own expressive porpoises.
9 years ago
<a href="../edit.html#12/stippling.frag"><img src="stippling-long.png" width="520px" height="200px"></img></a>
<a href="../edit.html#12/cell.frag"><img src="cell-long.png"></img></a>
9 years ago
9 years ago
![Deyrolle glass film](DeyrolleFilm.png)