prepare 11 and 12

This commit is contained in:
Wojtek 2023-02-07 16:58:19 +01:00
parent d9c55a1bda
commit d010ba0ad6
2 changed files with 600 additions and 0 deletions

340
11/README-pl.md Normal file
View File

@ -0,0 +1,340 @@
![NASA / WMAP science team](mcb.jpg)
## Szum (ang. "noise")
Czas na przerwę! Bawiliśmy się losowymi funkcjami, które wyglądają jak telewizyjny biały szum, w głowie wciąż się kręci myśląc o shaderach, a oczy są zmęczone. Czas wyjść na spacer!
Czujemy powietrze na skórze, słońce na twarzy. Świat jest tak żywym i bogatym miejscem. Kolory, tekstury, dźwięki. Podczas spaceru nie możemy nie zauważyć powierzchni dróg, skał, drzew i chmur.
<!-- ## Noise
It's time for a break! We've been playing with random functions that look like TV white noise, our head is still spinning thinking about shaders, and our eyes are tired. Time to go out for a walk!
We feel the air on our skin, the sun in our face. The world is such a vivid and rich place. Colors, textures, sounds. While we walk we can't avoid noticing the surface of the roads, rocks, trees and clouds. -->
![](texture-00.jpg)
![](texture-01.jpg)
![](texture-02.jpg)
![](texture-03.jpg)
![](texture-04.jpg)
![](texture-05.jpg)
![](texture-06.jpg)
Nieprzewidywalność tych tekstur można by nazwać "losową", ale nie przypominają one losowości, z którą bawiliśmy się wcześniej. "Prawdziwy świat" jest tak bogatym i złożonym miejscem! Jak możemy przybliżyć tę różnorodność obliczeniowo?
To było pytanie, które [Ken Perlin](https://mrl.nyu.edu/~perlin/) próbował rozwiązać we wczesnych latach 80-tych, kiedy otrzymał zlecenie wygenerowania bardziej realistycznych tekstur do filmu "Tron". W odpowiedzi na to wymyślił elegancki *Oscar winning* algorytm szumu. (No biggie.)
<!-- The unpredictability of these textures could be called "random," but they don't look like the random we were playing with before. The “real world” is such a rich and complex place! How can we approximate this variety computationally?
This was the question [Ken Perlin](https://mrl.nyu.edu/~perlin/) was trying to solve in the early 1980s when he was commissioned to generate more realistic textures for the movie "Tron." In response to that, he came up with an elegant *Oscar winning* noise algorithm. (No biggie.) -->
![Disney - Tron (1982)](tron.jpg)
Poniższe nie jest klasycznym algorytmem szumu Perlina, ale jest dobrym punktem wyjścia do zrozumienia sposobu generowania szumu.
<!-- The following is not the classic Perlin noise algorithm, but it is a good starting point to understand how to generate noise. -->
<div class="simpleFunction" data="
float i = floor(x); // integer
float f = fract(x); // fraction
y = rand(i); //rand() is described in the previous chapter
//y = mix(rand(i), rand(i + 1.0), f);
//y = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
"></div>
W tych liniach robimy coś podobnego do tego, co robiliśmy w poprzednim rozdziale. Dzielimy ciągłą liczbę zmiennoprzecinkową (``x``) na jej składowe całkowitą (``i``) i ułamkową (``f``). Używamy [``floor()``](../glossary/?search=floor) aby uzyskać ``i`` oraz [``fract()``](../glossary/?search=fract) aby uzyskać ``f```. Następnie stosujemy ``rand()`` do części całkowitej ``x``, co daje unikalną wartość losową dla każdej liczby całkowitej.
Po tym widzisz dwie skomentowane linie. Pierwsza z nich interpoluje liniowo każdą wartość losową.
<!-- In these lines we are doing something similar to what we did in the previous chapter. We are subdividing a continuous floating number (```x```) into its integer (```i```) and fractional (```f```) components. We use [```floor()```](../glossary/?search=floor) to obtain ```i``` and [```fract()```](../glossary/?search=fract) to obtain ```f```. Then we apply ```rand()``` to the integer part of ```x```, which gives a unique random value for each integer.
After that you see two commented lines. The first one interpolates each random value linearly. -->
```glsl
y = mix(rand(i), rand(i + 1.0), f);
```
Idź dalej i odkomentuj tę linię, aby zobaczyć jak to wygląda. Używamy [``fract()``](../glossary/?search=fract) wartości przechowywanych w `f` do [``mix()``](../glossary/?search=mix) dwóch losowych wartości.
W tym momencie książki nauczyliśmy się, że możemy zrobić coś lepszego niż interpolacja liniowa, prawda?
Spróbuj teraz odkomentować następującą linię, która używa interpolacji [``smoothstep()``](../glossary/?search=smoothstep) zamiast liniowej.
<!-- Go ahead and uncomment this line to see how this looks. We use the [```fract()```](../glossary/?search=fract) value store in `f` to [```mix()```](../glossary/?search=mix) the two random values.
At this point in the book, we've learned that we can do better than a linear interpolation, right?
Now try uncommenting the following line, which uses a [```smoothstep()```](../glossary/?search=smoothstep) interpolation instead of a linear one. -->
```glsl
y = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));
```
Po odkomentowaniu go zauważ, jak przejście między szczytami staje się gładkie. W niektórych implementacjach szumu można zauważyć, że programiści wolą kodować własne krzywe sześcienne (jak poniższy wzór) zamiast używać [``smoothstep()``](../glossary/?search=smoothstep).
<!-- After uncommenting it, notice how the transition between the peaks gets smooth. In some noise implementations you will find that programmers prefer to code their own cubic curves (like the following formula) instead of using the [```smoothstep()```](../glossary/?search=smoothstep). -->
```glsl
float u = f * f * (3.0 - 2.0 * f ); // custom cubic curve
y = mix(rand(i), rand(i + 1.0), u); // using it in the interpolation
```
Ta *płynna losowość* jest przełomem dla inżynierów graficznych i artystów - daje możliwość generowania obrazów i geometrii z organicznym uczuciem. Algorytm Szumu Perlina był wielokrotnie implementowany w różnych językach i wymiarach, aby stworzyć hipnotyzujące dzieła dla wszystkich rodzajów kreatywnych zastosowań.
<!--
This *smooth randomness* is a game changer for graphical engineers or artists - it provides the ability to generate images and geometries with an organic feeling. Perlin's Noise Algorithm has been implemented over and over in different languages and dimensions to make mesmerizing pieces for all sorts of creative uses. -->
![Robert Hodgin - Written Images (2010)](robert_hodgin.jpg)
Teraz twoja kolej:
* Stwórz własną funkcję ``float noise(float x)``.
* Użyj swojej funkcji szumu do animowania kształtu poprzez przesuwanie go, obracanie lub skalowanie.
* Zrób animowaną kompozycję kilku kształtów "tańczących" razem przy użyciu szumu.
* Skonstruuj "organicznie wyglądające" kształty używając funkcji noise.
* Gdy masz już swoje "stworzenie", spróbuj rozwinąć je w postać, przypisując mu określony ruch.
<!-- Now it's your turn:
* Make your own ```float noise(float x)``` function.
* Use your noise function to animate a shape by moving it, rotating it or scaling it.
* Make an animated composition of several shapes 'dancing' together using noise.
* Construct "organic-looking" shapes using the noise function.
* Once you have your "creature," try to develop it further into a character by assigning it a particular movement. -->
## 2D Noise
![](02.png)
Teraz, gdy wiemy jak zrobić szum w 1D, czas przejść do 2D. W 2D zamiast interpolować między dwoma punktami linii (``fract(x)`` i ``fract(x)+1. 0``), będziemy interpolować pomiędzy czterema narożnikami kwadratowego obszaru płaszczyzny (``fract(st)``, ``fract(st)+vec2(1.,0.)``, ``fract(st)+vec2(0.,1.)``` oraz ``fract(st)+vec2(1.,1.)``).
<!-- Now that we know how to do noise in 1D, it's time to move on to 2D. In 2D, instead of interpolating between two points of a line (```fract(x)``` and ```fract(x)+1.0```), we are going to interpolate between the four corners of the square area of a plane (```fract(st)```, ```fract(st)+vec2(1.,0.)```, ```fract(st)+vec2(0.,1.)``` and ```fract(st)+vec2(1.,1.)```). -->
![](01.png)
Podobnie, jeśli chcemy uzyskać szum 3D musimy interpolować pomiędzy ośmioma rogami sześcianu. W tej technice chodzi o interpolację losowych wartości, dlatego nazywa się ją **szumem wartościowym**.
<!-- Similarly, if we want to obtain 3D noise we need to interpolate between the eight corners of a cube. This technique is all about interpolating random values, which is why it's called **value noise**. -->
![](04.jpg)
Podobnie jak w przykładzie 1D, ta interpolacja nie jest liniowa, ale sześcienna, która płynnie interpoluje wszelkie punkty wewnątrz naszej kwadratowej siatki.
<!-- Like the 1D example, this interpolation is not linear but cubic, which smoothly interpolates any points inside our square grid. -->
![](05.jpg)
Przyjrzyj się następującej funkcji hałasu.
<!-- Take a look at the following noise function. -->
<div class="codeAndCanvas" data="2d-noise.frag"></div>
Zaczynamy od przeskalowania przestrzeni o 5 (linia 45), aby zobaczyć interpolację pomiędzy kwadratami siatki. Następnie wewnątrz funkcji szumu dzielimy przestrzeń na komórki. Przechowujemy całkowitą pozycję komórki wraz z pozycjami ułamkowymi wewnątrz komórki. Używamy pozycji całkowitej do obliczenia współrzędnych czterech narożników i otrzymujemy losową wartość dla każdego z nich (linie 23-26). Na koniec, w linii 35 interpolujemy pomiędzy 4 losowymi wartościami narożników używając pozycji frakcyjnych, które przechowywaliśmy wcześniej.
<!-- We start by scaling the space by 5 (line 45) in order to see the interpolation between the squares of the grid. Then inside the noise function we subdivide the space into cells. We store the integer position of the cell along with the fractional positions inside the cell. We use the integer position to calculate the four corners' coordinates and obtain a random value for each one (lines 23-26). Finally, in line 35 we interpolate between the 4 random values of the corners using the fractional positions we stored before. -->
Teraz twoja kolej. Spróbuj wykonać następujące ćwiczenia:
* Zmień mnożnik linii 45. Spróbuj go animować.
* Przy jakim poziomie powiększenia szum zaczyna znowu wyglądać jak losowy?
* Przy jakim poziomie powiększenia szum jest niezauważalny?
* Spróbuj podpiąć tę funkcję szumu do współrzędnych myszy.
* A gdyby tak potraktować gradient szumu jako pole odległości? Zrób z tym coś ciekawego.
* Teraz, gdy osiągnąłeś już pewną kontrolę nad porządkiem i chaosem, czas wykorzystać tę wiedzę. Stwórz kompozycję z prostokątów, kolorów i szumu, która przypomina nieco złożoność obrazu [Marka Rothko](http://en.wikipedia.org/wiki/Mark_Rothko).
<!-- Now it's your turn. Try the following exercises:
* Change the multiplier of line 45. Try to animate it.
* At what level of zoom does the noise start looking like random again?
* At what zoom level is the noise is imperceptible?
* Try to hook up this noise function to the mouse coordinates.
* What if we treat the gradient of the noise as a distance field? Make something interesting with it.
* Now that you've achieved some control over order and chaos, it's time to use that knowledge. Make a composition of rectangles, colors and noise that resembles some of the complexity of a [Mark Rothko](http://en.wikipedia.org/wiki/Mark_Rothko) painting. -->
![Mark Rothko - Three (1950)](rothko.jpg)
## Using Noise in Generative Designs
Algorytmy szumu zostały pierwotnie zaprojektowane w celu nadania naturalnego *je ne sais quoi* cyfrowym teksturom. Implementacje 1D i 2D, które widzieliśmy do tej pory, były interpolacjami pomiędzy losowymi *wartościami*, dlatego nazywane są **Value Noise**, ale istnieje więcej sposobów na uzyskanie szumu...
<!-- Noise algorithms were originally designed to give a natural *je ne sais quoi* to digital textures. The 1D and 2D implementations we've seen so far were interpolations between random *values*, which is why they're called **Value Noise**, but there are more ways to obtain noise... -->
[ ![Inigo Quilez - Value Noise](value-noise.png) ](../edit.php#11/2d-vnoise.frag)
Jak odkryłeś w poprzednich ćwiczeniach, szum wartości ma tendencję do wyglądania "blokowo". Aby zmniejszyć ten blokowy efekt, w 1985 roku [Ken Perlin](https://mrl.nyu.edu/~perlin/) opracował inną implementację algorytmu o nazwie **Gradient Noise**. Ken wymyślił jak interpolować losowe *gradienty* zamiast wartości. Gradienty te były wynikiem funkcji losowej 2D, która zwraca kierunki (reprezentowane przez ``vec2``) zamiast pojedynczych wartości (``float``). Kliknij na poniższy obrazek, aby zobaczyć kod i sposób jego działania.
<!-- As you discovered in the previous exercises, value noise tends to look "blocky." To diminish this blocky effect, in 1985 [Ken Perlin](https://mrl.nyu.edu/~perlin/) developed another implementation of the algorithm called **Gradient Noise**. Ken figured out how to interpolate random *gradients* instead of values. These gradients were the result of a 2D random function that returns directions (represented by a ```vec2```) instead of single values (```float```). Click on the following image to see the code and how it works. -->
[ ![Inigo Quilez - Gradient Noise](gradient-noise.png) ](../edit.php#11/2d-gnoise.frag)
Poświęć chwilę na przyjrzenie się tym dwóm przykładom autorstwa [Inigo Quilez](http://www.iquilezles.org/) i zwróć uwagę na różnice pomiędzy [value noise](https://www.shadertoy.com/view/lsf3WH) a [gradient noise](https://www.shadertoy.com/view/XdXGW8).
Podobnie jak malarz, który rozumie, jak działają pigmenty jego farb, im więcej wiemy o implementacjach szumu, tym lepiej będziemy mogli z nich korzystać. Na przykład, jeśli użyjemy dwuwymiarowej implementacji szumu do obrócenia przestrzeni, w której renderowane są linie proste, możemy uzyskać następujący efekt swirly, który wygląda jak drewno. Ponownie możesz kliknąć na obrazek, aby zobaczyć, jak wygląda kod.
<!-- Take a minute to look at these two examples by [Inigo Quilez](http://www.iquilezles.org/) and pay attention to the differences between [value noise](https://www.shadertoy.com/view/lsf3WH) and [gradient noise](https://www.shadertoy.com/view/XdXGW8).
Like a painter who understands how the pigments of their paints work, the more we know about noise implementations the better we will be able to use them. For example, if we use a two dimensional noise implementation to rotate the space where straight lines are rendered, we can produce the following swirly effect that looks like wood. Again you can click on the image to see what the code looks like. -->
[ ![Wood texture](wood-long.png) ](../edit.php#11/wood.frag)
```glsl
pos = rotate2d( noise(pos) ) * pos; // rotate the space
pattern = lines(pos,.5); // draw lines
```
Innym sposobem na uzyskanie ciekawych wzorów z szumu jest potraktowanie go jak pola odległości i zastosowanie niektórych sztuczek opisanych w rozdziale [Kształty](../07/).
<!-- Another way to get interesting patterns from noise is to treat it like a distance field and apply some of the tricks described in the [Shapes chapter](../07/). -->
[ ![Splatter texture](splatter-long.png) ](../edit.php#11/splatter.frag)
```glsl
color += smoothstep(.15,.2,noise(st*10.)); // Black splatter
color -= smoothstep(.35,.4,noise(st*10.)); // Holes on splatter
```
Trzecim sposobem wykorzystania funkcji szumu jest modulowanie kształtu. To również wymaga pewnych technik, które poznaliśmy w [rozdziale o kształtach](../07/).
<!-- A third way of using the noise function is to modulate a shape. This also requires some of the techniques we learned in the [chapter about shapes](../07/). -->
<a href="../edit.php#11/circleWave-noise.frag"><canvas id="custom" class="canvas" data-fragment-url="circleWave-noise.frag" width="300px" height="300"></canvas></a>
Dla Ciebie do poćwiczenia:
* Jaki inny wzór generatywny możesz stworzyć? Co z granitem? marmurem? magmą? wodą? Znajdź trzy zdjęcia interesujących Cię tekstur i zaimplementuj je algorytmicznie za pomocą szumu.
* Użyj szumu do modulacji kształtu.
* A co z wykorzystaniem szumu do ruchu? Wróć do rozdziału [Matrix](../08/). Użyj przykładu tłumaczenia, które przesuwa "+" wokół siebie i zastosuj do niego kilka *losowych* i *szumowych* ruchów.
* Zrób generatywnego Jacksona Pollocka.
<!-- For you to practice:
* What other generative pattern can you make? What about granite? marble? magma? water? Find three pictures of textures you are interested in and implement them algorithmically using noise.
* Use noise to modulate a shape.
* What about using noise for motion? Go back to the [Matrix chapter](../08/). Use the translation example that moves the "+" around, and apply some *random* and *noise* movements to it.
* Make a generative Jackson Pollock. -->
![Jackson Pollock - Number 14 gray (1948)](pollock.jpg)
## Improved Noise
Ulepszeniem Perlina do jego oryginalnego szumu **Simplex Noise**, jest zastąpienie sześciennej krzywej Hermite'a ( _f(x) = 3x^2-2x^3_ , która jest identyczna z funkcją [``smoothstep()``](../glossary/?search=smoothstep)) kwintową krzywą interpolacyjną ( _f(x) = 6x^5-15x^4+10x^3_ ). Dzięki temu oba końce krzywej są bardziej "płaskie", więc każda granica z wdziękiem zszywa się z następną. Innymi słowy, otrzymujesz bardziej ciągłe przejście między komórkami. Możesz to zobaczyć, odkomentowując drugą formułę w poniższym przykładzie wykresu (lub zobacz [dwa równania obok siebie tutaj](https://www.desmos.com/calculator/2xvlk5xp8b)).
<!-- An improvement by Perlin to his original non-simplex noise **Simplex Noise**, is the replacement of the cubic Hermite curve ( _f(x) = 3x^2-2x^3_ , which is identical to the [```smoothstep()```](../glossary/?search=smoothstep) function) with a quintic interpolation curve ( _f(x) = 6x^5-15x^4+10x^3_ ). This makes both ends of the curve more "flat" so each border gracefully stitches with the next one. In other words, you get a more continuous transition between the cells. You can see this by uncommenting the second formula in the following graph example (or see the [two equations side by side here](https://www.desmos.com/calculator/2xvlk5xp8b)). -->
<div class="simpleFunction" data="
// Cubic Hermite Curve. Same as SmoothStep()
y = x*x*(3.0-2.0*x);
// Quintic interpolation curve
//y = x*x*x*(x*(x*6.-15.)+10.);
"></div>
Zauważ, jak zmieniają się końce krzywej. Więcej na ten temat możesz przeczytać w [słowach własnych Kena](http://mrl.nyu.edu/~perlin/paper445.pdf).
<!-- Note how the ends of the curve change. You can read more about this in [Ken's own words](http://mrl.nyu.edu/~perlin/paper445.pdf). -->
## Simplex Noise
Dla Kena Perlina sukces jego algorytmu nie był wystarczający. Uważał, że może on działać lepiej. Na Siggraph 2001 zaprezentował "simplex noise", w którym osiągnął następujące ulepszenia w stosunku do poprzedniego algorytmu:
* Algorytm o mniejszej złożoności obliczeniowej i mniejszej liczbie mnożeń.
* Szum, który skaluje się do wyższych wymiarów przy mniejszym koszcie obliczeniowym.
* Szum bez artefaktów kierunkowych.
* Szum z dobrze zdefiniowanymi i ciągłymi gradientami, które mogą być obliczane dość tanio.
* Algorytm, który jest łatwy do zaimplementowania w sprzęcie.
<!-- For Ken Perlin the success of his algorithm wasn't enough. He thought it could perform better. At Siggraph 2001 he presented the "simplex noise" in which he achieved the following improvements over the previous algorithm:
* An algorithm with lower computational complexity and fewer multiplications.
* A noise that scales to higher dimensions with less computational cost.
* A noise without directional artifacts.
* A noise with well-defined and continuous gradients that can be computed quite cheaply.
* An algorithm that is easy to implement in hardware. -->
Wiem, co myślisz... "Kim jest ten człowiek?" Tak, jego praca jest fantastyczna! Ale poważnie, w jaki sposób ulepszył algorytm? Cóż, widzieliśmy jak dla dwóch wymiarów interpolował 4 punkty (rogi kwadratu); możemy więc poprawnie zgadnąć, że dla [trzech (zobacz implementację tutaj)](../edit.php#11/3d-noise.frag) i czterech wymiarów musimy interpolować 8 i 16 punktów. Prawda? Innymi słowy dla N wymiarów musisz płynnie interpolować 2 do N punktów (2^N). Ale Ken sprytnie zauważył, że chociaż oczywistym wyborem dla kształtu wypełniającego przestrzeń jest kwadrat, najprostszym kształtem w 2D jest trójkąt równoboczny. Zaczął więc od zastąpienia siatki kwadratowej (właśnie nauczyliśmy się jej używać) siatką simplex trójkątów równobocznych.
<!-- I know what you are thinking... "Who is this man?" Yes, his work is fantastic! But seriously, how did he improve the algorithm? Well, we saw how for two dimensions he was interpolating 4 points (corners of a square); so we can correctly guess that for [three (see an implementation here)](../edit.php#11/3d-noise.frag) and four dimensions we need to interpolate 8 and 16 points. Right? In other words for N dimensions you need to smoothly interpolate 2 to the N points (2^N). But Ken smartly noticed that although the obvious choice for a space-filling shape is a square, the simplest shape in 2D is the equilateral triangle. So he started by replacing the squared grid (we just learned how to use) for a simplex grid of equilateral triangles. -->
![](simplex-grid-00.png)
Kształt simplex dla N wymiarów to kształt z N + 1 narożami. Innymi słowy jeden mniejszy narożnik do obliczenia w 2D, 4 mniej narożniki w 3D i 11 mniej narożników w 4D! To ogromna poprawa!
W dwóch wymiarach interpolacja odbywa się podobnie do zwykłego szumu, poprzez interpolację wartości narożników odcinka. Ale w tym przypadku, dzięki zastosowaniu siatki simplex, musimy tylko interpolować sumę 3 narożników.
<!-- The simplex shape for N dimensions is a shape with N + 1 corners. In other words one fewer corner to compute in 2D, 4 fewer corners in 3D and 11 fewer corners in 4D! That's a huge improvement!
In two dimensions the interpolation happens similarly to regular noise, by interpolating the values of the corners of a section. But in this case, by using a simplex grid, we only need to interpolate the sum of 3 corners. -->
![](simplex-grid-01.png)
Jak powstaje siatka simplex? W kolejnym błyskotliwym i eleganckim posunięciu, siatkę simplex można uzyskać poprzez podział komórek regularnej czteroramiennej siatki na dwa trójkąty równoramienne, a następnie przekrzywianie jej, aż każdy trójkąt będzie równoboczny.
<!-- How is the simplex grid made? In another brilliant and elegant move, the simplex grid can be obtained by subdividing the cells of a regular 4 cornered grid into two isosceles triangles and then skewing it until each triangle is equilateral. -->
![](simplex-grid-02.png)
<!-- Then, as [Stefan Gustavson describes in this paper](http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf): _"...by looking at the integer parts of the transformed coordinates (x,y) for the point we want to evaluate, we can quickly determine which cell of two simplices that contains the point. By also comparing the magnitudes of x and y, we can determine whether the point is in the upper or the lower simplex, and traverse the correct three corner points."_
In the following code you can uncomment line 44 to see how the grid is skewed, and then uncomment line 47 to see how a simplex grid can be constructed. Note how on line 22 we are subdividing the skewed square into two equilateral triangles just by detecting if ```x > y``` ("lower" triangle) or ```y > x``` ("upper" triangle). -->
Wtedy, jak opisuje [Stefan Gustavson w tej pracy](http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf): _"...patrząc na części całkowite przekształconych współrzędnych (x,y) dla punktu, który chcemy ocenić, możemy szybko określić, która komórka dwóch prostopadłościanów zawiera ten punkt. Porównując również wielkości x i y, możemy określić, czy punkt znajduje się w górnej czy dolnej prostej i przemierzyć właściwe trzy punkty narożne."_.
W poniższym kodzie możesz odkomentować linię 44, aby zobaczyć jak siatka jest przekrzywiona, a następnie odkomentować linię 47, aby zobaczyć jak można skonstruować siatkę simpleksową. Zauważ jak w linii 22 dzielimy przekrzywiony kwadrat na dwa trójkąty równoboczne tylko poprzez wykrycie czy ``x > y`` (dolny" trójkąt) lub ``y > x`` (górny" trójkąt).
<div class="codeAndCanvas" data="simplex-grid.frag"></div>
<!-- All these improvements result in an algorithmic masterpiece known as **Simplex Noise**. The following is a GLSL implementation of this algorithm made by Ian McEwan and Stefan Gustavson (and presented in [this paper](http://webstaff.itn.liu.se/~stegu/jgt2012/article.pdf)) which is overcomplicated for educational purposes, but you will be happy to click on it and see that it is less cryptic than you might expect, and the code is short and fast. -->
Wszystkie te ulepszenia skutkują algorytmicznym arcydziełem znanym jako **Simplex Noise**. Poniżej znajduje się implementacja GLSL tego algorytmu wykonana przez Iana McEwana i Stefana Gustavsona (i przedstawiona w [tym artykule](http://webstaff.itn.liu.se/~stegu/jgt2012/article.pdf)), która jest nadmiernie skomplikowana w celach edukacyjnych, ale z przyjemnością klikniesz na nią i przekonasz się, że jest mniej krypto niż można by się spodziewać, a kod jest krótki i szybki.
[ ![Ian McEwan of Ashima Arts - Simplex Noise](simplex-noise.png) ](../edit.php#11/2d-snoise-clear.frag)
Cóż... dość technicznych rozważań, czas na wykorzystanie tego zasobu we własny, ekspresyjny sposób:
* Kontempluj, jak wygląda każda implementacja szumu. Wyobraź sobie je jako surowy materiał, jak marmurowy kamień dla rzeźbiarza. Co możesz powiedzieć o "uczuciu", jakie ma każda z nich? Zmruż oczy, aby uruchomić wyobraźnię, tak jak wtedy, gdy chcesz znaleźć kształty w chmurze. Co widzisz? Co ci się przypomina? W co wyobrażasz sobie, że każda realizacja hałasu mogłaby zostać wykonana? Podążając za swoimi wnętrznościami i spróbuj zrealizować to w kodzie.
* Zrób shader, który rzutuje iluzję przepływu. Jak lampa lawowa, krople atramentu, woda itp.
<!-- Well... enough technicalities, it's time for you to use this resource in your own expressive way:
* Contemplate how each noise implementation looks. Imagine them as a raw material, like a marble rock for a sculptor. What can you say about about the "feeling" that each one has? Squinch your eyes to trigger your imagination, like when you want to find shapes in a cloud. What do you see? What are you reminded of? What do you imagine each noise implementation could be made into? Following your guts and try to make it happen in code.
* Make a shader that projects the illusion of flow. Like a lava lamp, ink drops, water, etc. -->
<a href="../edit.php#11/lava-lamp.frag"><canvas id="custom" class="canvas" data-fragment-url="lava-lamp.frag" width="520px" height="200px"></canvas></a>
* Użyj Simplex Noise, aby dodać trochę tekstury do pracy, którą już wykonałeś.
<!-- * Use Simplex Noise to add some texture to a work you've already made. -->
<a href="../edit.php#11/iching-03.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-03.frag" width="520px" height="520px"></canvas></a>
W tym rozdziale wprowadziliśmy pewną kontrolę nad chaosem. Nie była to łatwa praca! Stanie się mistrzem noise-benderów wymaga czasu i wysiłku.
W następnych rozdziałach zobaczymy kilka dobrze znanych technik, które pozwolą ci udoskonalić swoje umiejętności i wydobyć więcej z szumu, aby zaprojektować wysokiej jakości treść generatywną za pomocą shaderów. Do tego czasu ciesz się czasem na zewnątrz, kontemplując naturę i jej zawiłe wzory. Twoja umiejętność obserwacji wymaga równego (a może nawet większego) poświęcenia niż twoje umiejętności tworzenia. Wyjdź na zewnątrz i ciesz się resztą dnia!
<!-- In this chapter we have introduced some control over the chaos. It was not an easy job! Becoming a noise-bender-master takes time and effort.
In the following chapters we will see some well known techniques to perfect your skills and get more out of your noise to design quality generative content with shaders. Until then enjoy some time outside contemplating nature and its intricate patterns. Your ability to observe needs equal (or probably more) dedication than your making skills. Go outside and enjoy the rest of the day! -->
<p style="text-align:center; font-style: italic;">"Talk to the tree, make friends with it." Bob Ross
</p>

260
12/README-pl.md Normal file
View File

@ -0,0 +1,260 @@
![](dragonfly.jpg)
## Cellular Noise
W 1996 roku, szesnaście lat po oryginalnym Noise Perlina i pięć lat przed jego Simplex Noise, [Steven Worley napisał pracę zatytułowaną "A Cellular Texture Basis Function"](http://www.rhythmiccanvas.com/research/papers/worley.pdf). Opisuje w niej technikę teksturowania proceduralnego, która jest obecnie szeroko stosowana przez społeczność grafików.
Aby zrozumieć jej zasady, musimy zacząć myśleć w kategoriach **iteracji**. Zapewne wiesz, co to oznacza: tak, zacznij używać pętli ``for``. Jest tylko jeden haczyk z pętlami ``for`` w GLSL: liczba, którą sprawdzamy musi być stałą (``const``). Tak więc nie ma dynamicznych pętli - liczba iteracji musi być stała.
Przyjrzyjmy się przykładowi.
<!-- In 1996, sixteen years after Perlin's original Noise and five years before his Simplex Noise, [Steven Worley wrote a paper called “A Cellular Texture Basis Function”](http://www.rhythmiccanvas.com/research/papers/worley.pdf). In it, he describes a procedural texturing technique now extensively used by the graphics community.
To understand the principles behind it we need to start thinking in terms of **iterations**. Probably you know what that means: yes, start using ```for``` loops. There is only one catch with ```for``` loops in GLSL: the number we are checking against must be a constant (```const```). So, no dynamic loops - the number of iterations must be fixed.
Let's take a look at an example. -->
### Points for a distance field
Hałas komórkowy opiera się na polach odległości, czyli odległości do najbliższego ze zbioru punktów charakterystycznych. Załóżmy, że chcemy stworzyć pole odległości składające się z 4 punktów. Co musimy zrobić? Cóż, **dla każdego piksela chcemy obliczyć odległość do najbliższego punktu**. Oznacza to, że musimy iterować po wszystkich punktach, obliczać ich odległości do bieżącego piksela i przechowywać wartość dla tego, który jest najbliższy.
<!-- Cellular Noise is based on distance fields, the distance to the closest one of a set of feature points. Let's say we want to make a distance field of 4 points. What do we need to do? Well, **for each pixel we want to calculate the distance to the closest point**. That means that we need to iterate through all the points, compute their distances to the current pixel and store the value for the one that is closest. -->
```glsl
float min_dist = 100.; // A variable to store the closest distance to a point
min_dist = min(min_dist, distance(st, point_a));
min_dist = min(min_dist, distance(st, point_b));
min_dist = min(min_dist, distance(st, point_c));
min_dist = min(min_dist, distance(st, point_d));
```
![](cell-00.png)
Nie jest to zbyt eleganckie, ale załatwia sprawę. Teraz zaimplementujmy go ponownie, używając tablicy i pętli ``for``.
<!-- This is not very elegant, but it does the trick. Now let's re-implement it using an array and a ```for``` loop. -->
```glsl
float m_dist = 100.; // minimum distance
for (int i = 0; i < TOTAL_POINTS; i++) {
float dist = distance(st, points[i]);
m_dist = min(m_dist, dist);
}
```
Zauważ, jak używamy pętli ``for`` do iteracji przez tablicę punktów i śledzenia minimalnej odległości za pomocą funkcji [``min()``](../glossary/?search=min). Oto krótka działająca implementacja tego pomysłu:
<!-- Note how we 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's a brief working implementation of this idea: -->
<div class="codeAndCanvas" data="cellnoise-00.frag"></div>
W powyższym kodzie jeden z punktów jest przypisany do pozycji myszy. Pobaw się nim, abyś mógł intuicyjnie zorientować się, jak zachowuje się ten kod. Następnie spróbuj tego:
- Jak można animować pozostałe punkty?
- Po przeczytaniu [rozdziału o kształtach](../07/), wyobraź sobie ciekawe sposoby wykorzystania tego pola odległości!
- Co, jeśli chcemy dodać więcej punktów do tego pola odległości? Co jeśli chcemy dynamicznie dodawać/odejmować punkty?
<!-- In the above code, one of the points is assigned to the mouse position. Play with it so you can get an intuitive idea of how this code behaves. Then try this:
- 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? -->
### Tiling and iteration
Zapewne zauważyłeś, że pętle ``for`` i *arrays* nie są zbyt dobrymi przyjaciółmi GLSL. Jak już wspomnieliśmy, pętle nie akceptują dynamicznych ograniczeń na ich warunek wyjścia. Ponadto, iteracja przez wiele instancji znacznie zmniejsza wydajność twojego shadera. Oznacza to, że nie możemy użyć tego bezpośredniego podejścia dla dużych ilości punktów. Musimy znaleźć inną strategię, taką, która wykorzystuje architekturę przetwarzania równoległego GPU.
<!-- You probably notice that ```for``` loops and *arrays* are not very good friends with GLSL. Like we said before, loops don't accept dynamic limits on their exit condition. Also, iterating through a lot of instances reduces the performance of your shader significantly. That means we can't use this direct approach for large amounts of points. We need to find another strategy, one that takes advantage of the parallel processing architecture of the GPU. -->
![](cell-01.png)
Jednym ze sposobów podejścia do tego problemu jest podzielenie przestrzeni na płytki. Nie każdy piksel musi sprawdzać odległość do każdego punktu, prawda? Biorąc pod uwagę fakt, że każdy piksel działa w swoim własnym wątku, możemy podzielić przestrzeń na komórki, z których każda ma jeden unikalny punkt do oglądania. Ponadto, aby uniknąć aberracji na krawędziach między komórkami musimy sprawdzić odległości do punktów na sąsiednich komórkach. To jest główna idea brillant [Steven Worley's paper](http://www.rhythmiccanvas.com/research/papers/worley.pdf). Na koniec każdy piksel musi sprawdzić tylko dziewięć pozycji: punkt własnej komórki i punkty w 8 komórkach wokół niego. Przestrzeń na komórki dzielimy już w rozdziałach o: [wzorach](../09/), [losowości](../10/) i [szumie](../11/), więc mam nadzieję, że jesteś już zaznajomiony z tą techniką.
<!-- One way to approach this problem is to divide the space into tiles. Not every pixel needs to check the distance to every single point, right? Given the fact that each pixel runs in its own thread, we can subdivide the space into cells, each one with one unique point to watch. Also, to avoid aberrations at the edges between cells we need to check for the distances to the points on the neighboring cells. That's the main brillant idea of [Steven Worley's paper](http://www.rhythmiccanvas.com/research/papers/worley.pdf). At the end, each pixel needs to check only nine positions: their own cell's point and the points in the 8 cells around it. We already subdivide the space into cells in the chapters about: [patterns](../09/), [random](../10/) and [noise](../11/), so hopefully you are familiar with this technique by now. -->
```glsl
// Scale
st *= 3.;
// Tile the space
vec2 i_st = floor(st);
vec2 f_st = fract(st);
```
Jaki jest więc plan? Użyjemy współrzędnych kafla (przechowywanych we współrzędnej całkowitej, ``i_st``) do skonstruowania losowej pozycji punktu. Funkcja ``random2f``, której użyjemy, otrzymuje ``vec2`` i daje nam ``vec2`` z losową pozycją. Tak więc, dla każdego kafla będziemy mieli jeden punkt charakterystyczny w losowej pozycji w obrębie kafla.
<!-- 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 receives a ```vec2``` and gives us a ```vec2``` with a random position. So, for each tile we will have one feature point in a random position within the tile. -->
```glsl
vec2 point = random2(i_st);
```
Każdy piksel wewnątrz tego kafla (przechowywany we współrzędnej float, ``f_st``) sprawdzi swoją odległość do tego losowego punktu.
<!-- 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);
```
Wynik będzie wyglądał tak:
<!-- The result will look like this: -->
<a href="../edit.php#12/cellnoise-01.frag"><img src="cellnoise.png" width="520px" height="200px"></img></a>
Nadal musimy sprawdzać odległości do punktów w okolicznych kaflach, a nie tylko do tego w bieżącym kaflu. W tym celu musimy **iterować** po sąsiednich kaflach. Nie wszystkie kafle, tylko te bezpośrednio otaczające bieżący. Czyli od ``-1`` (lewy) do ``1`` (prawy) kafla w osi ``x`` oraz od ``-1`` (dolny) do ``1`` (górny) w osi ``y``. Region 3x3 składający się z 9 płytek może być iterowany przy użyciu podwójnej pętli ``for``, jak ta:
<!-- We still need to check the distances to the points in the surrounding tiles, not just the one in the current tile. For that we need to **iterate** through the neighbor tiles. Not all tiles, just the ones immediately around the current one. That means from ```-1``` (left) to ```1``` (right) tile in ```x``` axis and ```-1``` (bottom) to ```1``` (top) in ```y``` axis. A 3x3 region of 9 tiles can be iterated through 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));
...
}
}
```
![](cell-02.png)
Teraz możemy obliczyć położenie punktów na każdym z sąsiadów w naszej podwójnej pętli ``for``, dodając przesunięcie sąsiedniego kafla do bieżącej współrzędnej kafla.
<!-- Now, we can compute 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. -->
```glsl
...
// Random position from current + neighbor place in the grid
vec2 point = random2(i_st + neighbor);
...
```
Reszta polega na obliczeniu odległości do tego punktu i zapisaniu najbliższego w zmiennej o nazwie ``m_dist`` (dla minimalnej odległości).
<!-- The rest is all about calculating the distance to that point and storing the closest one in a variable called ```m_dist``` (for minimum 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);
...
```
Powyższy kod jest inspirowany przez [ten artykuł Inigo's Quilez](http://www.iquilezles.org/www/articles/smoothvoronoi/smoothvoronoi.htm), gdzie powiedział:
*"... może warto zauważyć, że w tym kodzie powyżej jest ładna sztuczka. Większość implementacji cierpi z powodu problemów z precyzją, ponieważ generują swoje losowe punkty w przestrzeni "domeny" (jak przestrzeń "świata" lub "obiektu"), która może być dowolnie daleko od pochodzenia. Można rozwiązać ten problem przenosząc cały kod do typów danych o wyższej precyzji, lub będąc nieco sprytnym. Moja implementacja nie generuje punktów w przestrzeni "domeny", ale w przestrzeni "komórki": po wyodrębnieniu części całkowitych i ułamkowych punktu cieniowania, a zatem zidentyfikowaniu komórki, w której pracujemy, wszystko, co nas obchodzi, to to, co dzieje się wokół tej komórki, co oznacza, że możemy porzucić wszystkie całkowite części naszych współrzędnych, oszczędzając wiele bitów precyzji. W rzeczywistości, w zwykłej implementacji voronoi, całkowite części współrzędnych punktów po prostu znoszą się, gdy losowe punkty charakterystyczne na komórkę są odejmowane od punktu cieniowania. W powyższej implementacji nie pozwalamy nawet na to anulowanie, ponieważ przenosimy wszystkie obliczenia do przestrzeni "komórek". Ta sztuczka pozwala także na obsługę przypadku, gdy chcemy voronoi-shade'ować całą planetę - można po prostu zamienić dane wejściowe na podwójną precyzję, wykonać obliczenia floor() i fract(), a z resztą obliczeń przejść na zmiennoprzecinkowe bez ponoszenia kosztów zmiany całej implementacji na podwójną precyzję. Oczywiście, ta sama sztuczka dotyczy wzorców Perlin Noise (ale nigdy nie widziałem tego zaimplementowanego ani udokumentowanego gdziekolwiek). "*
<!-- The above code is inspired by [this article by Inigo's Quilez](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)."* -->
Rekapitulacja: dzielimy przestrzeń na kafelki; każdy piksel obliczy odległość do punktu w swoim własnym kafelku i otaczających go 8 kafelków; przechowuj najbliższą odległość. Wynikiem jest pole odległości, które wygląda jak w poniższym przykładzie:
<!-- Recapping: we subdivide the space into tiles; each pixel will calculate the distance to the point in their own tile and the surrounding 8 tiles; store the closest distance. The result is a distance field that looks like the following example: -->
<div class="codeAndCanvas" data="cellnoise-02.frag"></div>
Eksploruj to dalej przez:
- Skalowanie przestrzeni o różne wartości.
- Czy możesz wymyślić inne sposoby animacji punktów?
- Co jeśli chcemy obliczyć dodatkowy punkt z pozycji myszy?
- Jakie inne sposoby konstruowania tego pola odległości możesz sobie wyobrazić, poza ``m_dist = min(m_dist, dist);``?
- Jakie ciekawe wzory można stworzyć za pomocą tego pola odległości?
<!-- Explore this further by:
- Scaling the space by different values.
- Can you think of 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 can you imagine, besides ```m_dist = min(m_dist, dist);```?
- What interesting patterns can you make with this distance field? -->
Algorytm ten można również interpretować z perspektywy punktów, a nie pikseli. W takim przypadku można go opisać jako: każdy punkt rośnie, dopóki nie znajdzie rosnącego obszaru z innego punktu. Odzwierciedla to niektóre z zasad wzrostu w naturze. Żywe formy są kształtowane przez to napięcie pomiędzy wewnętrzną siłą do rozszerzania się i wzrostu, a ograniczeniami przez siły zewnętrzne. Klasyczny algorytm symulujący to zachowanie nosi nazwę [Georgy Voronoi](https://en.wikipedia.org/wiki/Georgy_Voronoy).
<!-- This algorithm can also be interpreted from the perspective of the points and not the pixels. In that case it can be described as: each point grows until it finds the growing area from another point. This mirrors some of the growth rules in nature. Living forms are shaped by this tension between an inner force to expand and grow, and limitations by outside forces. The classic algorithm that simulates this behavior is named after [Georgy Voronoi](https://en.wikipedia.org/wiki/Georgy_Voronoy). -->
![](monokot_root.jpg)
### Voronoi Algorithm
Konstruowanie diagramów Voronoi z szumu komórkowego jest mniej trudne niż mogłoby się wydawać. Musimy tylko *zachować* pewną dodatkową informację o dokładnym punkcie, który jest najbliżej piksela. Do tego celu użyjemy ``vec2`` o nazwie ``m_point``. Przechowując kierunek wektora do środka najbliższego punktu, zamiast tylko odległości, będziemy "przechowywać" "unikalny" identyfikator tego punktu.
<!-- Constructing Voronoi diagrams from cellular noise is less hard than what it might seem. We just need to *keep* some extra information about the precise point which is closest to the pixel. For that we are going to use a ```vec2``` called ```m_point```. By storing the vector direction to the center of the closest point, instead of just the distance, we will be "keeping" a "unique" identifier of that point. -->
```glsl
...
if( dist < m_dist ) {
m_dist = dist;
m_point = point;
}
...
```
Zauważ, że w poniższym kodzie nie używamy już ``min`` do obliczania najbliższej odległości, ale zwykłej deklaracji ``if``. Dlaczego? Ponieważ tak naprawdę chcemy zrobić coś więcej za każdym razem, gdy pojawi się nowy bliższy punkt, a mianowicie zapisać jego pozycję (linie 32 do 37).
<!-- Note that in the following code that we are no longer using ```min``` to calculate the closest distance, but a regular ```if``` statement. Why? Because we actually want to do something more every time a new closer point appears, namely store its position (lines 32 to 37). -->
<div class="codeAndCanvas" data="vorono-00.frag"></div>
Zauważ, jak kolor komórki ruchomej (związanej z pozycją myszy) zmienia kolor w zależności od jej położenia. To dlatego, że kolor jest przypisywany przy użyciu wartości (pozycji) najbliższego punktu.
Tak jak zrobiliśmy to wcześniej, teraz nadszedł czas, aby to przeskalować, przechodząc na podejście [Steven Worley's paper's approach](http://www.rhythmiccanvas.com/research/papers/worley.pdf). Spróbuj zaimplementować to samodzielnie. Możesz skorzystać z pomocy poniższego przykładu, klikając na niego. Zauważ, że oryginalne podejście Stevena Worleya używa zmiennej liczby punktów cech dla każdego kafla, więcej niż jeden w większości kafli. W jego implementacji programowej w C, jest to używane do przyspieszenia pętli poprzez wykonanie wczesnych wyjść. Pętle GLSL nie pozwalają na zmienną liczbę iteracji, więc prawdopodobnie chcesz trzymać się jednego punktu charakterystycznego na kafel.
<!-- Note how the color of the moving cell (bound to the mouse position) changes color according to its position. That's because the color is assigned using the value (position) of the closest point.
Like we did before, now is the time to scale this up, switching to [Steven Worley's paper's approach](http://www.rhythmiccanvas.com/research/papers/worley.pdf). Try implementing it yourself. You can use the help of the following example by clicking on it. Note that Steven Worley's original approach uses a variable number of feature points for each tile, more than one in most tiles. In his software implementation in C, this is used to speed up the loop by making early exits. GLSL loops don't allow variable number of iterations, so you probably want to stick to one feature point per tile. -->
<a href="../edit.php#12/vorono-01.frag"><canvas id="custom" class="canvas" data-fragment-url="vorono-01.frag" width="520px" height="200px"></canvas></a>
Gdy już rozgryziesz ten algorytm, pomyśl o ciekawych i kreatywnych jego zastosowaniach.
<!-- Once you figure out this algorithm, think of interesting and creative uses for it. -->
![Extended Voronoi - Leo Solaas (2011)](solas.png)
![Cloud Cities - Tomás Saraceno (2011)](saraceno.jpg)
![Accretion Disc Series - Clint Fulkerson](accretion.jpg)
![Vonoroi Puzzle - Reza Ali (2015)](reza.png)
### Improving Voronoi
W 2011 roku [Stefan Gustavson zoptymalizował algorytm Stevena Worleya dla GPU](http://webstaff.itn.liu.se/~stegu/GLSL-cellular/GLSL-cellular-notes.pdf), wykonując iterację tylko przez macierz 2x2 zamiast 3x3. To znacznie zmniejsza ilość pracy, ale może tworzyć artefakty w postaci nieciągłości na krawędziach między płytkami. Przyjrzyj się poniższym przykładom.
<!-- 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 through a 2x2 matrix instead of 3x3. This reduces the amount of work significantly, but it can create artifacts in the form of discontinuities at the edges between the tiles. 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" data-properties="clickRun:editor,openFrameIcon:false"></div>
Później w 2012 roku [Inigo Quilez napisał artykuł o tym, jak zrobić precyzyjne granice Voronoi](http://www.iquilezles.org/www/articles/voronoilines/voronoilines.htm).
<!-- Later in 2012 [Inigo Quilez wrote an article on how to make precise Voronoi borders](http://www.iquilezles.org/www/articles/voronoilines/voronoilines.htm). -->
<a href="../edit.php#12/2d-voronoi.frag"><img src="2d-voronoi.gif" width="520px" height="200px"></img></a>
Eksperymenty Inigo z Voronoi nie skończyły się na tym. W 2014 roku napisał ten ładny artykuł o tym, co nazywa [voro-noise](http://www.iquilezles.org/www/articles/voronoise/voronoise.htm), funkcją, która pozwala na stopniowe mieszanie się między zwykłym szumem a voronoi. Jego słowami:
*"Pomimo tego podobieństwa, faktem jest, że sposób użycia siatki w obu wzorach jest inny. Szum interpoluje/uśrednia wartości losowe (jak w szumie wartościowym) lub gradienty (jak w szumie gradientowym), podczas gdy Voronoi oblicza odległość do najbliższego punktu charakterystycznego. Teraz, interpolacja gładko-biliniowa i ocena minimalna to dwie bardzo różne operacje, czy... są? Czy być może można je połączyć w bardziej ogólną metrykę? Gdyby tak było, to zarówno Szum jak i Wzory Voronoi mogłyby być postrzegane jako szczególne przypadki bardziej ogólnego generatora wzorów opartego na siatce? "*.
<!-- Inigo's experiments with Voronoi didn't stop there. In 2014 he wrote this nice article about what he calls [voro-noise](http://www.iquilezles.org/www/articles/voronoise/voronoise.htm), a function that allows a gradual blend between regular noise and voronoi. In his words:
*"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?"* -->
<a href="../edit.php#12/2d-voronoise.frag"><canvas id="custom" class="canvas" data-fragment-url="2d-voronoise.frag" width="520px" height="200px"></canvas></a>
Teraz nadszedł czas, abyś przyjrzał się bliżej rzeczom, zainspirował się naturą i znalazł swoje własne ujęcie tej techniki!
<!-- Now it's time for you to look closely at things, be inspired by nature and find your own take on this technique! -->
![Deyrolle glass film - 1831](DeyrolleFilm.png)
<div class="glslGallery" data="12/metaballs,12/stippling,12/cell,12/tissue,12/cracks,160504143842" data-properties="clickRun:editor,openFrameIcon:false"></div>