Dobry generator liczb losowych generuje liczby, które nie są powiązane i nie układają się w opisywalny wzór. Powoli zaczynamy zauważać, że odrobina losowości może być dobrą rzeczą podczas programowania organicznych, imitujących życie zachowań. Jednak uznawanie losowości za pojedynczą wiodącą zasadę nie jest do końca naturalne.
Algorytm znany jako “Szum Perlina,”, nazwany w ten sposób na cześć jego twórcy Kena Perlina, daje bardziej naturalne wyniki. Perlin stworzył funkcję szumu pracując nad filmem Tron we wczesnych latach osiemdziesiątych; powstała ona aby tworzyć tekstury proceduralne do efektów wygenerowanych komputerowo. W 1997 roku Perlin zdobył za to Oscara w dziedzinie osiągnięć technicznych. Szum Perlina może być użyty w celu wygenerowania różnych efektów z naturalnymi cechami, takich jak chmury, krajobrazy i tekstury układające się we wzór, jak na przykład marmur.
Szum Perlina wygląda bardziej naturalnie, gdyż wytwarza naturalnie sortowane (“gładkie”) sekwencje liczb pseudo-losowych. Poniższy wykres przedstawia szum Perlina w zależności od czasu, gdzie oś x przedstawia czas; zauważcie, jak gładka jest ta krzywa.
Dla kontrastu na kolejnym wykresie przedstawiamy zwykłe generowanie liczb losowych w trakcie określonego czasu.
ProcessingJS posiada wbudowaną implementację algorytmu szumu Perlina: funkcję noise(). Funkcja noise() przyjmuje jeden, dwa lub trzy argumenty, w zależności od tego, czy szum generujemy w jednym, dwóch czy trzech wymiarach. Zacznijmy od szumu jednowymiarowego.

Szczegóły szumu

Dokumentacja szumu mówi nam, że szum jest obliczany podczas wielu “oktaw.” Wywoływanie funkcji noiseDetail() zmieni zarówno liczbę oktaw jak i ich zależność względem siebie. To z kolei wpływa na to, jak zachowuje się funkcja szumu.
Załóżmy, że chcemy narysować okrąg w naszym oknie ProcessingJS w losowym położeniu x:
var x = random(0, width);
ellipse(x, 180, 16, 16);
Teraz, zamiast losowych współrzędnych x, chcemy aby były one oparte na Szumie Perlina, które są bardziej “płynne.” Może wam się wydawać, że wystarczy tylko podmienić random() na noise() w następujący sposób:
var x = noise(0, width);
ellipse(x, 180, 16, 16);
Chociaż jest to dokładnie to, co chcieliśmy osiągnąć —wyznaczyć wartość x z przedziału pomiędzy 0 a szerokością na podstawie szumu Perlina—nie jest to poprawne podejście. Podczas, gdy funkcja random() określa zakres wartości pomiędzy minimum i maksimum, noise() nie działa w ten sposób. Zamiast tego, noise() oczekuje, że przekażemy parametr który określi "moment czasu" i zawsze zwraca wartość z zakresu pomiędzy 0 i 1. Możemy uważać jednowymiarowy szum Perlina za liniową sekwencję wartości w określonym czasie. Aby zademonstrować to w działaniu, oto przykłady podawanych parametrów i zwracanych wartości:
CzasWartość szumu
00,469
0,010,480
0,020,492
0,030,505
0,040,517
Aby uzyskać którąś z wartości szumu w ProcessingJS, musimy podać konkretny moment czasu do funkcji noise(). Dla przykładu:
var n = noise(0.03);
Na podstawie powyższej tabeli, noise() zwróci wartość 0,505 gdy czas wynosi 0,03. Moglibyśmy także napisać kod zapisuje wartość zmiennej opisującej czas i ciągle pytać o wartość szumu w metodzie draw().
Powyższy kod zwraca wciąż te same wartości. Ma to miejsce, ponieważ wciąż prosimy funkcję noise() o wynik w tym samym momencie czasu, w kółko.
Jeżeli jednak zwiększymy wartość zmiennej czasu t, otrzymamy inne wyniki.
To, jak szybko będziemy zwiększać t wpłynie na gładkość szumu. Jeżeli będziemy dokonywać dużych skoków w czasie, będziemy omijać pewne wartości przez co wyniki będą bardziej losowe.
Spróbujcie uruchomić powyższy kod parę razy, zwiększając t o 0.01, 0.02, 0.05, 0.1, 0.001, a zobaczycie różne wyniki.

Odwzorowanie szumu

Teraz jesteśmy już w stanie opisać co możemy zrobić z wartością szumu. Jeżeli mamy wartość z zakresu od 0 do 1, do nas należy odwzorowanie tego na zakres którego oczekujemy.
Moglibyśmy po prostu pomnożyć przez wartość maksymalną z naszego zakresu, ale ten moment to dobra okazja aby przedstawić Wam funkcję map() ProcessingJS, która pomoże nam w kolejnych symulacjach. Funkcja map() przyjmuje pięć argumentów. Pierwszym jest wartość którą chcemy odwzorować, w naszym wypadku n. Następne są wartości aktualnego zakresu(minimum i maksimum), a później wartości zakresu którego oczekujemy.
W tym przypadku wiemy, że szum ma zakres od 0 do 1, a chcemy narysować prostokąt o szerokości pomiędzy 0 a szerokością okna.
Możemy to samo zastosować do naszego obiektu Walker, opisując jego współrzędne x i y na podstawie szumu Perlina.
Zauważcie, że powyższe przykłady wymagają dodatkowej pary zmiennych: tx i ty. Jest to spowodowane tym, że musimy przechowywać informację o dwóch zmiennych czasu, jedna dla współrzędnej x obiektu Walker i jedną dla współrzędnej y.
Ale jest coś dziwnego w tych zmiennych. Czemu tx ma wartość początkową 0, a ty 10,000? Podczas gdy te liczby są narzucone z góry, nie jest przypadkiem to, że te zmienne zostały stworzone z różnymi wartościami. Jest to spowodowane tym, że funkcja szumu jest deterministyczna: daje ten sam wynik dla konkretnego czasu t za każdym razem. Gdybyśmy poprosili o wartość szumu dla zmiennej t zarówno dla x jak i y, x i y zawsze byłyby równe, przez co nasz obiekt Walker poruszałby się tylko wzdłuż przekątnej. Zamiast tego użyjemy dwóch różnych części wykresu szumu, zaczynając od 0 dla x i 10,000 dla y, dzięki czemu x i y będą wydawać się niezależne od siebie.
Tak na prawdę nie ma tutaj prawdziwego pojęcia czasu. To przydatna metafora która pozwoliła nam zrozumieć jak działa funkcja szumu, ale tak na prawdę mamy przestrzeń, a nie czas. Powyższy wykres przedstawia liniową sekwencję wartości w jednowymiarowej przestrzeni, a my możemy poprosić o wartość dla konkretnej wartości x kiedy chcemy. W przykładach często zauważycie zmienną xoff opisującą przesunięcie wartości x wzdłuż wykresu szumu zamiast zmiennej t opisującej czas (tak jak jest to opisane na tym wykresie).
W następnym wyzwaniu możecie spróbować używać szumu z Walkerem w trochę inny sposób. Miłej zabawy!

Ten kurs "Symulacje Natury" jest pochodną z "Natury Kodu " stworzonej przez Daniela Shiffmana, użytej pod licencją Creative Commons Attribution-NonCommercial 3.0 Unported License.
Ładowanie