voro
This commit is contained in:
parent
8517e5c4a1
commit
5a8f7a23f6
82
12/README.md
82
12/README.md
|
|
@ -31,9 +31,9 @@ Note in the above code, that one of the points is the mouse position. Play with
|
|||
### 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. We need another stratergy.
|
||||
Also iterating through a lot of instances reduce the performance of your shader significantly. So... We need to find another stratergy.
|
||||
|
||||
One way to aproach 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. Thats was the original approach of [Steven Worley's paper](http://www.rhythmiccanvas.com/research/papers/worley.pdf). Let's start subdivide the space into cells like we did before in the [patterns](../09/), [random](../10/) and [noise](../11/) chapters, hopefully by now you are familiarize with this technique.
|
||||
One way to aproach 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.
|
||||
|
||||
```glsl
|
||||
// Scale
|
||||
|
|
@ -44,23 +44,79 @@ One way to aproach this problem is to divide the space in tiles. Not every pixel
|
|||
vec2 f_st = fract(st);
|
||||
```
|
||||
|
||||
In the above code we subdivide the space in a 3x3 grid.
|
||||
|
||||
Well if you have to scale the previus example with a bigger set of points you will discover that is actually very hard to do that in an efficient way. The solution involves tiling the space like we have done before.
|
||||
|
||||
<div class="codeAndCanvas" data="cellnoise-01.frag"></div>
|
||||
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 recive a ```vec2``` and give us a ```vec2``` with a random position. So for each tile we will have one point in a random position.
|
||||
|
||||
```glsl
|
||||
...
|
||||
m_dist = min(m_dist, m_dist*dist);
|
||||
...
|
||||
vec2 point = random2(i_st);
|
||||
```
|
||||
|
||||
Each pixel inside that tile (stored in the float coordinate, ```f_st```) will check their distance to that random point.
|
||||
|
||||
```glsl
|
||||
vec2 diff = point - f_st;
|
||||
float dist = length(diff);
|
||||
```
|
||||
|
||||
This will look like this:
|
||||
|
||||
<a href="../edit.html#12/cellnoise-01.frag"><img src="cellnoise.png" width="520px" height="200px"></canvas></a>
|
||||
|
||||
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 inmediatly arround 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:
|
||||
|
||||
```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));
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, we can predict the position of the points on each one of the neightbors in our double ```for``` loop by adding the neighboor tile offset to the current tile coordinate.
|
||||
|
||||
```
|
||||
...
|
||||
// Random position from current + neighbor place in the grid
|
||||
vec2 point = random2(i_st + neighbor);
|
||||
...
|
||||
```
|
||||
|
||||
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 precission 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 precission 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 fractioanl 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 substracted 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 precission, 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 precission. Of course, same trick applies to Perlin Noise patterns (but i've never seen it implemented nor documented anywhere)."*
|
||||
|
||||
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?
|
||||
- Try to replicate this metaballs example:
|
||||
|
||||
<a href="../edit.html#12/metaballs.frag"><canvas id="custom" class="canvas" data-fragment-url="metaballs.frag" width="520px" height="200px"></canvas></a>
|
||||
|
||||
[Inigo Quilez voronoi borders article](http://www.iquilezles.org/www/articles/smoothvoronoi/smoothvoronoi.htm)
|
||||
|
||||
*"Before continuing, it might be worth noting that there's a nice trick in this code above. Most implementations out there suffer from precission 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 precission 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 fractioanl 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 substracted 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 precission, 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 precission. Of course, same trick applies to Perlin Noise patterns (but i've never seen it implemented nor documented anywhere)."*
|
||||
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 clasic algorithm that simulate this begaviur is named after [Georgy Voronoi](https://en.wikipedia.org/wiki/Georgy_Voronoy).
|
||||
|
||||
### Voronoi Algorithm
|
||||
|
||||
|
|
|
|||
|
|
@ -25,41 +25,22 @@ void main() {
|
|||
vec2 i_st = floor(st);
|
||||
vec2 f_st = fract(st);
|
||||
|
||||
float m_dist = 1.; // minimun distance
|
||||
vec2 point = random2(i_st);
|
||||
vec2 diff = point - f_st;
|
||||
|
||||
for (int j= -1; j <= 1; j++) {
|
||||
for (int i= -1; i <= 1; i++) {
|
||||
// Neighbor place in the grid
|
||||
vec2 neighbor = vec2(float(i),float(j));
|
||||
|
||||
// Random position from current + neighbor place in the grid
|
||||
vec2 offset = random2(i_st + neighbor);
|
||||
|
||||
// Animate the offset
|
||||
offset = 0.5 + 0.5*sin(u_time + 6.2831*offset);
|
||||
|
||||
// Position of the cell
|
||||
vec2 pos = neighbor + offset - f_st;
|
||||
|
||||
// Cell distance
|
||||
float dist = length(pos);
|
||||
|
||||
// Keep the closer distance
|
||||
m_dist = min(m_dist, dist);
|
||||
}
|
||||
}
|
||||
float dist = length(diff);
|
||||
|
||||
// Draw the min distance (distance field)
|
||||
color += m_dist;
|
||||
color += dist;
|
||||
|
||||
// Draw cell center
|
||||
color += 1.-step(.02, m_dist);
|
||||
color += 1.-step(.02, dist);
|
||||
|
||||
// Draw grid
|
||||
color.r += step(.98, f_st.x) + step(.98, f_st.y);
|
||||
|
||||
// Show isolines
|
||||
// color -= step(.7,abs(sin(27.0*m_dist)))*.5;
|
||||
// color -= step(.7,abs(sin(27.0*dist)))*.5;
|
||||
|
||||
gl_FragColor = vec4(color,1.0);
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
// Author: @patriciogv
|
||||
// Title: CellularNoise
|
||||
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
uniform vec2 u_resolution;
|
||||
uniform vec2 u_mouse;
|
||||
uniform float u_time;
|
||||
|
||||
vec2 random2( vec2 p ) {
|
||||
return fract(sin(vec2(dot(p,vec2(127.1,311.7)),dot(p,vec2(269.5,183.3))))*43758.5453);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 st = gl_FragCoord.xy/u_resolution.xy;
|
||||
st.x *= u_resolution.x/u_resolution.y;
|
||||
vec3 color = vec3(.0);
|
||||
|
||||
// Scale
|
||||
st *= 3.;
|
||||
|
||||
// Tile the space
|
||||
vec2 i_st = floor(st);
|
||||
vec2 f_st = fract(st);
|
||||
|
||||
float m_dist = 1.; // minimun distance
|
||||
|
||||
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));
|
||||
|
||||
// Random position from current + neighbor place in the grid
|
||||
vec2 point = random2(i_st + neighbor);
|
||||
|
||||
// Animate the point
|
||||
point = 0.5 + 0.5*sin(u_time + 6.2831*point);
|
||||
|
||||
// Vector between the pixel and the point
|
||||
vec2 diff = neighbor + point - f_st;
|
||||
|
||||
// Distance to the point
|
||||
float dist = length(diff);
|
||||
|
||||
// Keep the closer distance
|
||||
m_dist = min(m_dist, dist);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the min distance (distance field)
|
||||
color += m_dist;
|
||||
|
||||
// Draw cell center
|
||||
color += 1.-step(.02, m_dist);
|
||||
|
||||
// Draw grid
|
||||
color.r += step(.98, f_st.x) + step(.98, f_st.y);
|
||||
|
||||
// Show isolines
|
||||
// color -= step(.7,abs(sin(27.0*m_dist)))*.5;
|
||||
|
||||
gl_FragColor = vec4(color,1.0);
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
|
|
@ -26,30 +26,27 @@ void main() {
|
|||
vec2 f_st = fract(st);
|
||||
|
||||
float m_dist = 10.; // minimun distance
|
||||
vec2 m_pos; // minimum position
|
||||
vec2 m_point; // minimum point
|
||||
|
||||
for (int j=-1; j<=1; j++ ) {
|
||||
for (int i=-1; i<=1; i++ ) {
|
||||
vec2 cell = vec2(float(i),float(j));
|
||||
vec2 offset = random2(i_st + cell);
|
||||
|
||||
offset = 0.5 + 0.5*sin(u_time + 6.2831*offset);
|
||||
|
||||
vec2 pos = cell + offset - f_st;
|
||||
float dist = length(pos);
|
||||
vec2 neighbor = vec2(float(i),float(j));
|
||||
vec2 point = random2(i_st + neighbor);
|
||||
point = 0.5 + 0.5*sin(u_time + 6.2831*point);
|
||||
vec2 diff = neighbor + point - f_st;
|
||||
float dist = length(diff);
|
||||
|
||||
if( dist < m_dist ) {
|
||||
m_dist = dist;
|
||||
m_pos = offset;
|
||||
m_point = point;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Assign a color using the closest cell position
|
||||
color += dot(m_pos,vec2(.3,.6));
|
||||
// Assign a color using the closest point position
|
||||
color += dot(m_point,vec2(.3,.6));
|
||||
|
||||
// Add distance field to closest cell center
|
||||
// Add distance field to closest point center
|
||||
// color.g = m_dist;
|
||||
|
||||
// Show isolines
|
||||
|
|
|
|||
Loading…
Reference in New Issue