Створюємо реалістичний ландшафт за 130 рядків коду на javascript

Створюємо реалістичний ландшафт за 130 рядків коду на javascript

Програмісти - ледачі істоти з тонкою душевною організацією, що допомагає нам знаходити прості і красиві рішення задач з мінімальними витратами. У цій статті ми створимо реалістичний ландшафт за допомогою алгоритму «diamond-square». Ми не будемо довго промальовувати вручну кам'янистий рельєф, який в підсумку, швидше за все, виявиться вельми убогим. Замість цього, завдяки генерації фракталів, ми навчимо комп'ютер, що означає бути каменем.

Карта висот

Будемо зберігати ландшафт у вигляді карти висот - двовимірного масиву, в якому міститься інформація про висоту кожної точки місцевості за координатами x і y. За допомогою цієї простої структури даних можна візуалізувати висоту як завгодно - з Canvas, WebGL і т.д. Основне обмеження полягає в тому, що ми не можемо відображати вертикальні отвори ландшафту, такі як печери або тунелі.

Цей алгоритм можна застосовувати до сітки будь-якого розміру, але зручніше за все використовувати квадрат розміру ступеня двійки + 1. Ми будемо використовувати одне і те ж значення size для осей x. y і z. оформляючи наш ландшафт в куб. Конвертуємо detail в ступінь двійки + 1, щоб при більш докладної деталізації генерувалися куби більшого розміру.

Візьмемо квадрат. Розділимо його ще на чотири квадрати і зрушимо їх центри вгору або вниз випадковим чином. Розділимо кожний з них ще на квадрати і повторимо, раз по раз зменшуючи діапазон випадкового зсуву для отримання більш дрібних деталей.

Це алгоритм «diamond-square». У нашому випадку він трохи вдосконалений для отримання більш реалістичного результату: простір поперемінно ділиться на квадрати (squares) і ромби (diamonds).

встановлюємо кути

Спочатку потрібно встановити кутах початкове значення seed, яке вплине на решту візуалізацію. Код нижче підніме всі кути на половину висоти куба:

ділимо карту

Тепер будемо рекурсивно спостерігати за все меншими поділами карти висот. При кожному діленні ми будемо розбивати карту на квадрати і оновлювати положення центральної точки кожного з них під час фази «square». Потім ми розділимо карту на ромби і оновимо їх центральні точки на етапі «diamond».

Використання змінної scale гарантує, що величина зрушень зменшується разом з величиною поділів. Для кожного ділення ми множимо поточний розмір на коефіцієнт нерівності roughness. який визначає, буде ландшафт гладким (значення близько 0) або гірським (значення близько 1).

Обидві форми (square і diamond) працюють за одним принципом, але отримують дані з різних точок. На фазі square перед випадковим зсувом ми знаходимо середнє від чотирьох кутових точок, а на фазі diamond - від чотирьох точок на ребрах.

візуалізація

Цей алгоритм лише дає нам дані, які ми вже можемо візуалізувати різними способами. Тут ми сумісний кілька технік для створення растрової ізометричної 3D-проекції ландшафтної карти на сітці.

Задом-наперед

Спочатку ми створюємо вкладені цикли, які витягують прямокутники з «задньої частини» нашої карти (y = 0) «вперед» (y = this.size). Такий же цикл ми б використовували для візуалізації простого плоского квадрата.

Наш невибагливий підхід забезпечує гарну візуальну текстуру. Ми порівнюємо поточну висоту з висотою наступної точки, щоб обчислити схил. І малюємо більш яскраві прямокутники для більш високих схилів, щоб заповнити одну сторону світлом, а іншу - тінню.

ізометрична проекція

Візуально цікавіше перевести наш ландшафт з фази «square» в фазу «diamond» перш ніж робити його 3D-проекцію. Ізометрична проекція зводить верхній лівий і нижній правий кути в центр зображення.

Центральна (перспективна) проекція

Ми будемо використовувати так само просту 3D-проекцію для конвертації значень x. y і z в плоску картинку з перспективою на 2D-екрані.

Основна ідея будь-якій проекції перспективи полягає в тому, щоб розділити горизонтальну і вертикальну позицію на глибину таким чином, щоб більш далекі об'єкти здавалися меншими.

збираємо воєдино

Створюємо новий екземпляр Terrain з необхідним рівнем деталізації. Потім генеруємо його карту висот зі значенням нерівності (roughness) між 0 і 1. Нарешті, переносимо ландшафт на сітку.