Гідравлічна ерозія: як річки формують гори
Сирий heightmap із шуму Перліна (згенерована комп'ютером карта висот із випадковим, але плавним шумом) виглядає як зім'ята фольга — горбистий, але мертвий. Гідравлічна ерозія — це алгоритм, який перетворює його на переконливий ландшафт: симульовані краплі дощу підхоплюють наноси на шляху вниз, вирізають долини й осідають там, де вода сповільнюється, — той самий фізичний процес, що сформував кожну річкову дельту та каньйон на Землі.
1. Чому самого шуму недостатньо
Heightmap, згенерований лише з шуму Перліна / fBm, має фатальну візуальну ваду: кожен пагорб виглядає так само, як і будь-який інший. Реальний рельєф анізотропний — вода завжди тече вниз найкрутішим шляхом, тож гори накопичують гострі хребти й річкові долини, вирізані за мільйони років, тоді як рельєф лише з шуму не має жодної «напрямної пам'яті».
Гідравлічна ерозія виправляє це, буквально симулюючи рух води по рельєфу, яка підхоплює і осаджує наноси на своєму шляху. Прогнавши кілька сотень тисяч симульованих крапель дощу над heightmap, хребти загострюються, долини поглиблюються, а деревоподібні річкові мережі виникають повністю з фізики — без ручних правил розгалуження.
Моделі на основі сітки (Musgrave та ін., 1989) відстежують об'єм води й наносів у кожній комірці heightmap і легко запускаються на GPU. Моделі на основі часток/крапель (Beyer, 2015; Hjelle, 2017) симулюють окремі краплі дощу, що котяться вниз, — простіші в реалізації, і саме на цьому підході зосереджена ця стаття.
2. Модель ерозії краплями
Кожна крапля — це маленька симульована частка з позицією, напрямком, швидкістю, об'ємом води та вантажем наносів. На кожному кроці вона:
- Зчитує висоту та градієнт рельєфу під собою (білінійна інтерполяція між 4 сусідніми комірками сітки)
- Оновлює свій напрямок — частково слідуючи градієнту вниз, частково зберігаючи попередній імпульс (інерція)
- Робить один крок у цьому напрямку
- Обчислює, скільки наносів вода здатна нести за своєї нової швидкості (ємність наносів)
- Або розмиває рельєф (якщо нижче ємності), або осаджує наноси (якщо вище ємності)
- Втрачає частину води через випаровування
де ∇h — градієнт рельєфу в позиції краплі, а inertia ∈ [0, 1] (типово 0.05) обмінює чутливість до градієнта на плавніші, природніші русла
Крапля «вмирає», коли повністю випаровується, стікає за межі мапи або перевищує максимальний час життя (типово 30–60 кроків). Для ерозії всієї мапи симулюють мільйони таких дешевих незалежних крапель.
3. Ємність наносів і рівняння транспорту
Основна фізична ідея, запозичена з реальної флювіальної геоморфології: швидка глибока вода несе більше наносів, ніж повільна мілка. Ємність наносів краплі на кожному кроці:
де: Δh — зміна висоти на кроці (від'ємна = вниз), minSlope — нижня межа, що не дає ємності занулитись на пласкій ділянці, speed — швидкість краплі, water — залишок води, Kc — константа ємності наносів (параметр налаштування)
Ця єдина формула пояснює більшість річкової геоморфології одразу: круті верхні ділянки течуть швидко й агресивно розмивають, вирізаючи V-подібні долини; коли схил вирівнюється ближче до узбережжя, ємність падає, і річка осаджує свій вантаж, будуючи дельти й заплави.
4. Ерозія, осадження та випаровування
Порівняння поточного вантажу наносів s з ємністю C визначає, чи крапля розмиває, чи осаджує:
якщо s < C: розмити min((C − s) · erodeRate, −Δh) з рельєфу під краплею, розподіливши по невеликому радіусу пензля, і додати до s
Ерозія через «пензель» (м'який круговий спад, типово радіус 2–4 комірки), а не в одну точку, уникає гострих одноточкових артефактів ранніх реалізацій і дає плавніші стінки русел.
типове evaporateRate ≈ 0.01 за крок — крапля поступово висихає, змушуючи її осадити рештки наносів перед тим, як зникнути
5. Теплова ерозія — осипи та щебінь
Сама лише гідравлічна ерозія може залишати схили крутіші, ніж здатен витримати будь-який реальний матеріал, — прямовисні уступи з пухкого ґрунту під 80°. Реальні схили також формуються тепловою (гравітаційною) ерозією: цикли замерзання-відтавання та гравітація повільно зсувають пухкий матеріал донизу, доки кожен схил не досягне кута природного укосу — максимального нахилу, який здатен утримувати сипкий матеріал, перш ніж посипатись.
якщо slope(c, n) > tanThresholdAngle:
Δmove = (slope − threshold) · c · settlingRate
перенести Δmove матеріалу з c до n
Застосована як дешевий постпроцес після гідравлічної ерозії, теплова ерозія згладжує нереалістично гострі кручі й породжує характерні осипні схили (фартухи пухкого каміння) біля підніжжя реальних гір.
6. Псевдокод
Повне життя однієї краплі (спрощено за реалізацією Hjelle, 2017):
function simulateDroplet(heightmap, startPos):
pos = startPos
dir = { x: 0, y: 0 }
speed = 1
water = 1
sediment = 0
for step in 0..maxLifetime:
// 1. Зчитати висоту + градієнт (білінійно)
[height, gradient] = sampleHeightAndGradient(heightmap, pos)
// 2. Оновити напрямок з інерцією, потім рухатись
dir = dir * inertia - gradient * (1 - inertia)
dir = normalize(dir)
pos += dir
if outOfBounds(pos): break
// 3. Обчислити різницю висот після руху
newHeight = sampleHeight(heightmap, pos)
deltaH = newHeight - height
// 4. Ємність наносів на цьому кроці
capacity = max(-deltaH, minSlope) * speed * water * Kc
if sediment > capacity or deltaH > 0:
// Осадити надлишок наносів
amount = deltaH > 0
? min(deltaH, sediment)
: (sediment - capacity) * depositRate
sediment -= amount
deposit(heightmap, pos, amount)
else:
// Розмити рельєф, обмежено доступним перепадом висоти
amount = min((capacity - sediment) * erodeRate, -deltaH)
erode(heightmap, pos, amount, brushRadius)
sediment += amount
// 5. Оновити швидкість і випаровування
speed = sqrt(speed*speed + deltaH * gravity)
water *= (1 - evaporateRate)
if water < minWaterThreshold: break
7. Масштабування: ерозія на сітці та GPU
Модель крапель вище паралелізується легко по краплях, але дві краплі, що пишуть в одну комірку одночасно, — це стан гонки, незручний для GPU. Два поширені рішення:
- Пакетні CPU-краплі: симулювати краплі послідовно на CPU (або невеликими партіями без перетину), як роблять більшість офлайн-інструментів для рельєфу (World Machine, Gaea) для heightmap 1024×1024 за кілька секунд.
- Ерозія на сітці — модель мілкої води (Mei, Decaudin & Hu, 2007): замість дискретних крапель зберігається неперервне поле висоти води по всій сітці, яке крокує вперед за спрощеним рівнянням мілкої води — це природно лягає на WebGL fragment shaders / compute shaders і розмиває весь рельєф паралельно кожен кадр.
Для рельєфу в реальному часі в браузері (як у WebGL-демо на цьому сайті) стандартний вибір — ерозія мілкої води на сітці як пінг-понг прохід render-target: краплинну ерозію легше зрозуміти, але важче паралелізувати на GPU.
🏔️ Спробуйте симуляцію рельєфу
Інтерактивний WebGL-рельєф, побудований на шарах шуму Перліна — досліджуйте хребти й долини, які вирізав би алгоритм ерозії з цієї статті.
Відкрити симуляцію →