Процедурно генерируемые карты мира на Unity C#, часть 4 (трафик)
Биомы — это способ классификации типов земной поверхности. Наш генератор биомов будет основан на популярной модели Уиттекера, в которой биомы классифицируются по количеству осадков и температуре. Мы уже сгенерировали тепловую карту и карту влажности для нашего мира, поэтому определение биомов будет довольно просто выполнить. Схема классификации Уиттекера представлена на следующей иллюстрации:
Мы можем разделить различные типы биомов по заданной температуре и уровню влажности. Сначала создадим новое перечисление, в котором будут храниться эти типы биомов:
Затем нужно создать таблицу, которая поможет нам определить тип биома на основании температуры и влажности. У нас уже есть HeatType и MoistureType. Каждое из этих перечислений содержит 6 определенных типов. Для сопоставления каждого из этих типов со схемой Уиттекера создана следующая таблица:
Для удобства поиска этих данных в коде преобразуем таблицу в двухмерный массив. Он будет таким:
Чтобы еще больше упростить поиск, добавим новую функцию, возвращающую тип биома любого тайла. Эта часть довольно проста, ведь каждому тайлу уже назначен тип тепла и влажности.
Эта проверка выполняется для каждого тайла и устанавливает области биомов для всей карты.
Отлично, мы определили все биомы. Однако пока мы не можем их визуализировать. Следующим шагом будет назначение цвета каждому типу. Это позволит нам наглядно отобразить область каждого биома на изображении. Я выбрал следующие цвета:
Значения цветов вставлены в класс TextureGenerator вместе с кодом генерирования текстуры биомов:
При рендеринге карт биомов получаются красивые сворачиваемые карты мира.
Генерирование сферических карт
До этого момента мы создавали миры, сворачиваемые по оси X и Y. Такие карты отлично подходят для игр, потому что данные легко рендерятся в игровую карту.
Если попытаться спроектировать такие сворачиваемые текстуры на сферу, они будут выглядеть странно. Чтобы наш мир мог накладываться на сферу, необходимо написать генератор сферических текстур. В этой части мы добавим такую функцию для сгенерированных нами миров.
Сферическое генерирование немного отличается от генерирования свертываемых карт, потому что требует других шумовых схем и наложения текстур. По этой причине мы разделим класс генератора на две ветви подклассов: WrappableWorldGenerator и SphericalWorldGenerator. Каждый из них будет наследовать базовый класс Generator.
Это позволит нам иметь общее функциональное ядро, предоставляющее расширенные возможности каждому типу генератора.
Исходный класс Generator, а также некоторые его функции станут абстрактными:
Имеющиеся у нас функции Initialize() и GetData() были созданы для сворачиваемых миров, поэтому для сферического генератора нужно написать новые. Также мы создадим новые классы получения тайлов, потому что свертывание будет происходит на оси X со сферическим проецированием.
Инициализация шума происходит способом, похожим на ранее описанный, за исключением одного главного отличия. Тепловая карта в новом генераторе не будет свертываться на оси Y. Поэтому мы не можем создать правильный градиент для умножения. Нам придется делать это вручную во время генерирования данных.
Функция GetData изменится значительно. Мы вернемся к сэмплированию трехмерного шума. Шум будет сэмплироваться на основании системы координат с широтой и долготой.
Я посмотрел, как выполнили сферическое проецирование в libnoise, и использовал ту же концепцию. Основной код, преобразующий координаты широты и долготы в декартовы координаты трехмерной сферической карты, будет следующим:
Функция GetData циклически переберет все координаты, используя этот способ преобразования для генерирования данных карты. С помощью этого способа мы создаем данные тепла, высоты и влажности. Карта биомов генерируется так же, как и раньше — из конечных тепловой карты и карты влажности.
Мы получаем, соответственно, карту высот, тепловую карту, карту влажности и карту биомов:
Заметьте, что карты изгибаются возле углов. Это сделано специально, так работает сферическое проецирование. Давайте применим текстуру биомов для сферы и посмотрим, что получится:
Неплохое начало. Обратите внимание, наша карта высот стала черно-белой. Мы сделали это для того, чтобы использовать карту высот в качестве шейдера сферы. Для лучшего эффекта нам необходимо рельефная текстура, поэтому мы сначала отрендерим черно-белую текстуру, отображающую нужные нам смещения. Эта текстура затем будет преобразована в рельефную текстуру с помощью следующего кода:
Передав этой функции левую текстуру, мы получим рельефную текстуру, изображенную справа:
Теперь если мы применим эту рельефную карту вместе с картой высот через стандартный шейдер к нашей сфере, мы получим следующее:
Чтобы еще улучшить изображение, мы добавим пару слоев облаков. Сгенерировать облака с помощью шума очень просто, так почему бы и нет. Мы используем модуль волнового (billow) шума для создания облаков.
Добавим два слоя облаков, чтобы придать им глубины. Код генератора облачного шума представлен ниже:
Мы используем данные таким же способом. Генератор текстур облаков — это простой линейный интерполятор (lerp) от белого до прозрачного белого. Мы отсекаем облака до установленного значения, делая все остальное прозрачным. Код генератора текстур облаков имеет следующий вид:
Создадим с его помощью две различные текстуры облаков. Эти текстуры тоже создаются для сферического проецирования, поэтому имеют изгибы по краям:
Теперь добавим два сферических меша немного большего размера, чем исходная сфера. Применив текстуры облаков к стандартному шейдеру с эффектом затухания (fade), мы получим красиво выглядящую облачность:
В конце я привожу скриншот всех сгенерированных текстур, использованных для создания финального рендера планеты:
На этом серия статей заканчивается. Исходный код всего проекта на github: World Generator Final.