diff --git a/11/README-pl.md b/11/README-pl.md new file mode 100644 index 0000000..2f25c08 --- /dev/null +++ b/11/README-pl.md @@ -0,0 +1,340 @@ + + + +## 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. + + + + + + + + + + + +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.) + + + + + +Poniższe nie jest klasycznym algorytmem szumu Perlina, ale jest dobrym punktem wyjścia do zrozumienia sposobu generowania szumu. + + + +
+ +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ą. + + + + +```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. + + + +```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). + + + +```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ń. + + + + + +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. + + + +## 2D Noise + + + +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.)``). + + + + + +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**. + + + + + +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. + + + + + +Przyjrzyj się następującej funkcji hałasu. + + + + + +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. + + + +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). + + + + + +## 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... + + + +[  ](../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. + + + +[  ](../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. + + + +[  ](../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/). + + + +[  ](../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/). + + + + + +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. + + + + + +## 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)). + + + + + +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). + + + + +## 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. + + + +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. + + + + + +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. + + + + + +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. + + + + + + + +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). + + + + + +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. + +[  ](../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. + + + + + +* Użyj Simplex Noise, aby dodać trochę tekstury do pracy, którą już wykonałeś. + + + + + +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! + + + +"Talk to the tree, make friends with it." Bob Ross +
diff --git a/12/README-pl.md b/12/README-pl.md new file mode 100644 index 0000000..436ac5c --- /dev/null +++ b/12/README-pl.md @@ -0,0 +1,260 @@ + + +## 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. + + + +### 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. + + + +```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)); +``` + + + +Nie jest to zbyt eleganckie, ale załatwia sprawę. Teraz zaimplementujmy go ponownie, używając tablicy i pętli ``for``. + + + +```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: + + + + + +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? + + + +### 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. + + + + + +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ą. + + + +```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. + + + +```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. + + + +```glsl + vec2 diff = point - f_st; + float dist = length(diff); +``` + +Wynik będzie wyglądał tak: + + + +
+
+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:
+
+
+
+```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));
+ ...
+ }
+}
+```
+
+
+
+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.
+
+
+
+```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).
+
+
+
+```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). "*
+
+
+
+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:
+
+
+
+
+
+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?
+
+
+
+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).
+
+
+
+
+
+### 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.
+
+
+
+```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).
+
+
+
+
+
+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.
+
+
+
+
+
+Gdy już rozgryziesz ten algorytm, pomyśl o ciekawych i kreatywnych jego zastosowaniach.
+
+
+
+
+
+
+
+
+
+
+
+### 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.
+
+
+
+
+
+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).
+
+
+
+
+
+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? "*.
+
+
+
+
+
+Teraz nadszedł czas, abyś przyjrzał się bliżej rzeczom, zainspirował się naturą i znalazł swoje własne ujęcie tej techniki!
+
+
+
+
+
+