Введение

Добро пожаловать в мой второй пост в блоге! Мы продолжим наше путешествие глубже в мир процедурной генерации. В этом блоге мы будем создавать нашу собственную процедурную сетку ландшафта в Unity! Моя конечная цель — научить всех генерировать сетку ландшафта, как показано на изображении выше.

Прежде чем мы начнем программировать, я расскажу все, что нам нужно знать о создании сетки ландшафта. Темы, которые я затрону сегодня: геометрия, основы генерации сетки, шум, карты высот, и я закончу подведением итогов.

Как всегда, расслабьтесь и наслаждайтесь приключением!

Геометрия

Чтобы процедурно сгенерировать сетку, важно, чтобы мы ознакомились с некоторыми основными геометрическими понятиями, как описано ниже.

Квадратный четырехугольник (квадрат)

Для создания нашей сетки ландшафта мы будем использовать квадратные четырехугольники (quads). Квадратный четырехугольник — это двумерная фигура, состоящая из 4 вершин, 4 ребер и 1 грани, как показано выше.

Основы генерации сетки

Компьютерная графика и треугольники

В компьютерной графике все фигуры состоят из треугольников. Для этого есть несколько причин, но в первую очередь это связано с тем, что треугольники очень эффективны с точки зрения памяти, и они могут отображаться очень быстро. Как показано выше, моя сетка ландшафта состоит из нескольких квадратных четырехугольников, и каждый из этих четырехугольников состоит ровно из двух треугольников.

Каждый треугольник содержит 3 вершины. В Unity мы можем создать треугольник, объявив массив int, где каждый элемент этого массива будет соответствовать точке вершины.

int[] triangles = new int[]
{
  0,1,2 //corresponds the vertex points at 0, 1, and 2
};

В Unity и многих других движках элементы в этом массиве int должны соответствовать вершинам в порядке по часовой стрелке, что относится к Back-Face Culling.

Отбор лицевой стороны

В компьютерной графике метод под названием Back-Face Culling используется для повышения эффективности за счет того, что видны только передние треугольники.

Треугольник считается фронтальным, если он соответствует вершинам, расположенным по часовой стрелке. Тогда как, если треугольники соответствуют вершинам в порядке против часовой стрелки, треугольник будет либо не виден, либо неправильно отрисован на экране.

Ниже я привел примеры для лучшей визуализации использования неправильных индексов треугольников по сравнению с правильными индексами треугольников для создания четырехугольника.

Создание квадратного четырехугольника

Как я упоминал ранее, каждый квадратный четырехугольник состоит из двух треугольников, каждый из которых соответствует трем вершинам. Нижний левый треугольник (заштрихованный зеленым) соответствует точкам 0, 1 и 2 вершины. Верхний правый треугольник (заштрихованный синим цветом) соответствует 2, 1 и 3 точкам вершины. Оба этих треугольника имеют общие вершины в точках 1 и 2.

Ниже приведен фрагмент кода для создания вышеуказанного четырехугольника.

Атрибуты вершин

В основном все данные в нашей сетке поступают непосредственно из атрибутов вершин. Каждая вершина содержит несколько атрибутов, но нас больше всего интересуют атрибуты позиции, цвета и нормали.

Положение: положение вершины.

Цвет. Цвет вершины.

Normals:определяет направление вершины и позволяет правильно ее затенить.

Общие вершины против неразделенных вершин

Как общие вершины, так и неразделенные вершины служат своей цели. Каждый из этих случаев будет влиять на то, как меш затеняется и связан с нормалями вершин.

Неразделенные вершины приведут к четким краям между треугольниками. Это полезно для объектов с острыми краями, таких как куб. Чтобы вершины не были разделены, вершины ребер должны быть удвоены, чтобы каждый треугольник имел свой собственный набор уникальных вершин.

Общие вершины вызывают интерполяцию нормалей вершин, что приводит к сглаживанию краев между треугольниками. Это полезно для создания изогнутой поверхности, такой как наша сетка ландшафта. Чтобы использовать общие вершины, мы просто повторно используем вершины ребер, чтобы каждый треугольник разделял некоторые из своих вершин с другим треугольником.

Изучая общие вершины и неразделяемые вершины, я обнаружил, что это очень запутанно, поэтому я предоставил наглядные примеры ниже, чтобы помочь вам лучше понять эту концепцию.

Компоненты сетки

Чтобы хранить данные и визуализировать нашу сетку в Unity, мы должны прикрепить к нашему игровому объекту как Mesh Filter, так и Mesh Renderer. Без этих двух компонентов мы не сможем создать нашу сетку.

Фильтр сетки: сохраняет данные для сетки.

Mesh Renderer: использует данные из фильтра Mesh для визуализации сетки на экране.

Генерация сетки ландшафта

Итак, теперь, когда мы знаем основы создания меша, мы можем приступить к созданию нашего меша ландшафта.

Чтобы сгенерировать нашу сетку, мы создадим сетку из четырехугольников. Отсюда мы будем манипулировать высотой вершин, что сделает нашу сетку похожей на ландшафт.

Для управления значениями высоты вершин мы создадим алгоритм шум, который будем интерпретировать как нашу карту высот.

Шум

Ладно, я знаю, о чем ты думаешь. Какое отношение шум имеет к генерации ландшафтов и всего такого? Оказывается, шум может быть очень полезен в генеративных алгоритмах!

Случайный шум

Этот тип шума имеет случайные значения, которые не связаны друг с другом и не очень полезны с точки зрения процедурной генерации.

Перлин Шум

Во время работы над оригинальным фильмом TRON в начале 1980-х профессор по имени Кен Перлин разработал функцию шума, чтобы компьютерные изображения (CGI) выглядели более реалистично. Этот алгоритм называется шумом Перлина, и он генерирует псевдослучайные значения, которые связаны друг с другом. Результат шума Перлина имеет более органичный вид, в отличие от случайного шума, где значения не связаны между собой.

Шум Перлина в Unity

В Unity есть встроенная реализация шума Перлина, и мы будем использовать ее для создания нашей сетки ландшафта.

Шум Перлина требует два параметра для координат X и Y и возвращает дробное значение от 0 до 1. В этом случае я предоставил координату Z вместо координаты Y, потому что мы имеем дело с осями X и Z для наша сетка ландшафта.

Важно знать, что шум Перлина всегда будет возвращать одно и то же значение для любого целочисленного значения, переданного в качестве координат. Чтобы избежать этого, мы должны сначала преобразовать оси X и Z в числа с плавающей запятой, прежде чем передавать их в функцию шума Перлина.

Входные значения шума Перлина

Чтобы еще больше изменить наш алгоритм шума, мы можем передать значения. Ниже я перечислил типичные входные значения, которые используются с шумом Перлина.

Масштаб: управляет уровнем шума. При уменьшении масштаба шум уменьшается. Принимая во внимание, что увеличение масштаба приведет к увеличению шума.

Октавы. Относится к использованию нескольких уровней шума. 1-я октава является базовым слоем и обеспечивает общую форму шума. Каждая последующая октава будет увеличивать детализацию шума.

Лакунарность: контролирует частоту каждой октавы шума, которая влияет на количество деталей, добавляемых для каждой октавы.

Постоянство:управляет амплитудой каждой октавы шума, что определяет влияние каждой октавы на общую форму.

Seed: псевдослучайное значение, используемое для смещения значения шума, чтобы оно выглядело по-разному каждый раз при его создании. Если мы не изменим начальное значение, сгенерированный шум будет точно таким же, и это может быть полезно для целей отладки.

Сравнение шума

Случайный шум очень случайный, и каждое сгенерированное значение не имеет абсолютно никакой корреляции с предыдущим или следующим сгенерированным значением. Что касается создания ландшафта, мы используем шум Перлина, потому что различные значения соотносятся друг с другом, придавая ландшафту более органичный и естественный вид.

Я предоставил два снимка экрана ниже для лучшего сравнения между ландшафтом, сгенерированным с использованием случайного шума (1-е изображение), и ландшафтом, сгенерированным с использованием шума Перлина (2-е изображение).

Карты высот

Карта высот — это двумерный массив значений, обычно изображение в оттенках серого, где каждый пиксель содержит данные о высоте. Белые области представляют большую высоту, тогда как черные области представляют низкую высоту. Серые области представляют базовую или нейтральную высоту. Для нашей сетки ландшафта мы можем напрямую интерпретировать наш алгоритм шума как нашу карту высот.

Резюме блога

Итак, я знаю, что говорил о многих разных вещах в этом сообщении в блоге, но я дам краткое изложение того, что нам на самом деле нужно сделать для создания нашей сетки ландшафта.

Подготовительный этап

Для начала нам нужно решить, будут ли размеры карты измеряться в четырехугольниках или вершинах.

Шаг 1

Далее мы начнем с создания 2D-сетки вершин по осям X и Z.

Шаг 2

После создания наших вершин мы хотим создать наши четырехугольники. При этом нам нужно создать треугольники, соединяющие наши вершины из шага 1.

Шаг 3

На этом этапе, если мы все сделали правильно, у нас должна получиться плоская сетка из четырехугольников. Теперь мы хотим начать работать над нашим алгоритмом шума.

Шаг 4

Используя несколько входных значений, мы можем создать наш алгоритм шума и дать ему желаемый результат.

Шаг 5

Теперь, когда мы создали наш алгоритм шума, мы можем напрямую интерпретировать его как нашу карту высот и применить его к значению высоты наших вершин.

Шаг 6

Затем мы можем создать значение множителя высоты, чтобы дополнительно контролировать высоту наших вершин. На данный момент наша сетка должна теперь выглядеть как гористая местность.

Шаг 7

Наконец, мы хотим придать нашей сетке ландшафта некоторый цвет. Для этого мы создаем массив цветов, где каждый цвет соответствует каждой вершине сетки.

Точная настройка

Хорошо, теперь у нас есть основа для нашего меша ландшафта. Отсюда мы можем точно настроить нашу сетку, изменив переменные для получения более желаемого результата.

Демонстрационное видео

Я предоставил видео о том, как я создаю процедурную сетку ландшафта в Unity. Если на вашем ПК установлена ​​Unity, не стесняйтесь следовать!

Если вы хотите взглянуть на мои скрипты из этого видео, я создал для них репозиторий на GitHub: https://github.com/kp4ws/ProceduralTerrain

Вывод

Наконец все хорошее должно закончиться. Надеюсь, вам понравилось приключение моей серии блогов, состоящей из двух частей, в рамках Procedural Generation. В своем первом блоге я дал вам общий обзор PCG и того, как его можно использовать в разработке игр. В этом втором блоге я углубился и научил вас, как процедурно генерировать сетку ландшафта.

Используя творческий подход, вы можете расширить то, чему я вас научил, и подумать о различных способах реализации процедурной генерации в своем путешествии по разработке игр.

Хорошего дня!

использованная литература

https://youtube.com/playlist?list=PLFt_AvWsXl0eBW2EiBtl_sxmDtSgZBxB3

https://catlikecoding.com/unity/tutorials/noise-derivatives/

https://gamedevacademy.org/complete-guide-to-procedural-level-generation-in-unity-part-1/

https://docs.unity3d.com/ScriptReference/Mathf.PerlinNoise.html

https://www.redblobgames.com/articles/noise/introduction.html

https://www.youtube.com/watch?v=ucuOVL7c5Hw&list=PL5KbKbJ6Gf9-d303Lk8TGKCW-t5JsBdtB

https://www.redblobgames.com/maps/terrain-from-noise/

https://www.youtube.com/watch?v=64NblGkAabk

https://en.wikipedia.org/wiki/Back-face_culling

https://www.scratchapixel.com/lessons/procedural-generation-virtual-worlds/perlin-noise-part-2

https://docs.unity3d.com/Manual/AnatomyofaMesh.html

https://en.wikipedia.org/wiki/Кен_Перлин

https://en.wikipedia.org/wiki/Perlin_noise

https://medium.com/@yvanscher/playing-with-perlin-noise-generating-realistic-archipelagos-b59f004d8401