Szum przypominający nic, aż go się warstwuje
Tworzenie wiarygodnego krajobrazu zaczyna się od szumu gradientowego (szum Perlinowski lub szum prostokątny): gładka, pseudo-randomowa funkcja położenia bez ostrych skoków, generowana deterministycznie z ustalonego ziarna, dzięki czemu zawsze (x, y) zwraca tę samą wartość wysokości. Sam szum gradientowy wygląda jak płaskie, faliste, pozbawione szczegółów wzgórza – niczym prawdziwy system górski, który posiada detale na wielu różnych skalach jednocześnie.
Pasmowa Ruchomość Fraktalna: nakładanie ośmiotoków
Pasmowa Ruchomość Fraktalna (PRF) rozwiązuje to, poprzez sumowanie kilku ośmiotoków tej samej funkcji szumu o rosnącej częstotliwości i malejącej amplitudzie – każdy tok dodaje więcej szczegółów, jak np. nakładanie się skał na szczytach gór lub formacje skalnych w pasmach górskich:
funkcja fbm(x, y, ośmiotoki, lacunarność, zysk) { float suma = 0, amplituda = 1, częstotliwość = 1, maxValue = 0; for (int i = 0; i < ośmiotoki; i++) { suma += noise(x * częstotliwość, y * częstotliwość) * amplituda; maxValue += amplituda; amplituda *= zysk; // zwykle 0.5 - każdy tok słabszy częstotliwość *= lacunarność; // zwykle 2.0 - każdy tok dokładniejszy } return suma / maxValue; // normalizowany do około [-1, 1] }
function fbm(x, y, octaves, lacunarity, gain) {
let total = 0, amplitude = 1, frequency = 1, maxValue = 0;
for (let i = 0; i < octaves; i++) {
total += noise(x * frequency, y * frequency) * amplitude;
maxValue += amplitude;
amplitude *= gain; // typically 0.5 - each octave weaker
frequency *= lacunarity; // typically 2.0 - each octave finer
}
return total / maxValue; // normalised to roughly [-1, 1]
}
Od wysokości do biomów: załamanie i kolorowanie
Sam mapę wysokości to szara plama; staje się czytelnym krajobrazem, gdy zostanie odpowiednio załamany i zabarwiony. Załamanie oblicza lokalny kąt nachylenia powierzchni z różnic wysokości między sąsiednimi komórkami i mnoży go przez stały kierunek słońca, oświetlając zbocza skierowane ku światłu i ciemniając te skierowane od niego – ta sama technika, której używali kartografowie ręcznie od XIX wieku. Kolorowanie biomów następnie definiuje progi wysokości (i czasem drugiego, niezależnego warstwy szumu dla wilgotności) w pasma: głębokie błękity poniżej poziomu morza, piasek w pobliżu wybrzeża, zielony na umiarkowanej wysokości, szarość skał i biały śnieg na szczytach.
Erozja hydrauliczna: tworzenie wrażenia wyżarzonych form
Surowe fBm nigdy nie powoduje erozji, dlatego jego linie grzbietów wygląda na zawsze jednako miękko, w przeciwieństwie do prawdziwego terenu, rzeźbionego przez miliony lat spływającej wody. Erozja hydrauliczna symuluje to wyżarzanie bezpośrednio: rozpyla tysiące wirtualnych kropli wody na wysokościowym mapie i pozwala każdej z nich podążać za lokalnym nachyleniem w dół, zbierając osad tam, gdzie przepływa szybko po stromych obszarach i odkładając go, gdy spowalnia w niższych miejscach.
Dla każdej z N kropli: pozycja = losowy punkt na mapie póki krzept jest aktywna: nachylenie = nachylenie_wysokościowe(pozycja) // kierunek najdłuższego spadku pozycja += -nachylenie * prędkość nośność = prędkość * woda * współczynnik_nachylenia jeśli osad > nośność: odkładaj(pozycja, osad - nośność) w przeciwnym razie: eroduj(pozycja, nośność - osad) prędkość = aktualizacja w oparciu o stratę wysokości (zasada zachowania energii) woda *= parowanie // krzept przestaje działać Uruchom ten proces dla tysięcy kropli, aby wygenerować rozpoznawalne doliny i odłożyć falki dokładnie tam, gdzie spływająca woda by to zrobiła, całkowicie z deterministycznej wysokościowej mapy i losowego zestawu punktów upuszczenia – nie jest wymagany ręcznie wprowadzony dane rzecznych. Ponieważ jest to kosztowne na kroplę, implementacje w czasie rzeczywistym uruchamiają je jako opcjonalny etap przetwarzania poobwodowy zamiast każdego klatki, a generują podstawowe bloki fBm kawałek po kawałku, gdy kamera przesuwa się, dzięki czemu świat jest zasadniczo nieskończony bez konieczności przechowywania całej mapy w pamięci jednocześnie.
for each of N droplets:
pos = random point on the map
while droplet has not stopped:
grad = heightGradient(pos) // steepest descent direction
pos += -grad * speed
capacity = speed * water * slopeFactor
if sediment > capacity: deposit(pos, sediment - capacity)
else: erode(pos, capacity - sediment)
speed = update from height loss (energy conservation)
water *= evaporation // droplet eventually stops
Frequently asked questions
Dlaczego teren generowany z jednego warstwy szumu wygląda nienaturalnie?
Jedna warstwa szumu ma szczegółowość tylko w jednej skali przestrzennej, więc wygląda jak gładkie, pozbawione cech pagórki. Prawdziwy teren ma strukturę na wielu skalach jednocześnie – duże góry, średnie grzbiety, małe kamienie – co dokładnie odzwierciedla sumowanie kilku októw szumu o różnych częstotliwościach (fBm).
Jakie jest różnice między lacunarnością a zyskiem w szumie fBm?
Lacunarność kontroluje, jak bardzo drobniejsza staje się częstotliwość z każdym dodanym oktą (zwykle podwajając); zysk kontroluje, jak słabszy jest amplituda każdej nowej oktę (zwykle dzieląc przez 2). Razem określają, ile szczegółów drobnych warstw nakłada się na ogólną formę i tym samym, czy teren będzie gładki czy szorstki.
Czy erozja hydrologiczna rzeczywiście symuluje wodę fizycznie?
Jest to uproszczona, oparta na cząstkach aproksymacja, a nie pełny solver płynu: każdy kroplet podąża za najostrzejszym lokalnym nachyleniem i wymienia osad w zależności od swojej prędkości i pojemności, bez symulowania ciśnienia ani turbulencji. Jest wystarczająco tania, aby działać interaktywnie, a jednocześnie generować wiarygodne doliny i depozyjne falki.
Wypróbuj na żywo
Wszystko powyżej działa bezpośrednio w Twojej przeglądarce — otwórz Procedural Terrain i zmieniaj parametry podczas działania. Nic nie jest instalowane ani przesyłane na serwer, cały model działa w jednej karcie.
▶ Otwórz symulację Procedural Terrain