generation
September 30, 2024

Демо сцена. Базовые цвета. Часть 1.

Telegram: Infinity World

Введение

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

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


1. Типы вокселей

Воксель (англ. Voxel) - это небольшой кусочек данных, который описывает пространство. Его можно представить как кубик, например, размером (зависит от LOD) в 1x1x1 unit. Этот кубик хранит в себе информацию о SDF для генерации формы ландшафта, а также некоторую информацию по материалам.

Рисунок 1. Воксель. Взято из wikipedia.org.

1.1 Воксель материалы

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

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

  • Voxel Type: обобщенный идентификатор, который хранит некоторую общую информацию о типе вокселя, например, текстовый идентификатор (имя). Напрямую тип вокселя в алгоритмах не используется, так как это было бы очень дорого.
  • Voxel Type ID: это целочисленный идентификатор (short), который указывает на глобальный индекс типа вокселя в наборе типов вокселей. Так как это просто число, то его можно использовать как на стороне CPU, так и на стороне GPU, а также по нему легко (за O(1)) получить сам тип вокселя или другие данные, которые к нему относятся.
  • Voxel Type Data: локальные, для каждого вокселя, данные, состоящие из идентификатора (Voxel Type ID) и веса. Вес используется во множестве алгоритмов: от определения, что может быть сгенерировано на поверхности, до текстур ландшафта.

Voxel Type и Voxel Type ID определяются в Editor и не могут изменены в runtime, а вот Voxel Type Data определяется для каждого вокселя в процессе генерации каждого чанка на стороне GPU. Но так как в каждом вокселе может быть до 4-х разных типов, даже в соседних, при генерации меша ландшафта, необходимо выбрать те типы вокселей, которые имеют наибольший вес.

Для каждой вершины я также выбираю до 4-х типов вокселей: каждая вершина зависит от 9 вокселей (один центральный и 8 угловых), поэтому я беру в сумме 36 типов вокселей и выбираю из них самые весовые, заодно вычисляю средневзвешенные веса для этих четырех, которые потом нормализуются и используются во всех остальных алгоритмах. Нормализация весов тут важна, так как, во избежание артефактов, сумма весов выбранных типов вокселей должна быть равна единице.

Веса типов вокселей хранятся в Vertex Layout, а вот Voxel IDs хранятся в отдельном per primitive буфере, так как они не могут интерполироваться. Если же хранить в вершинах, то придется создавать hard-edges, что приведет к существенному увеличению потребления памяти.

Сами же материалы вокселей определяются независимо от типов вокселей. Каждый материал хранит ссылки на текстуры (base color, normal, smoothness, metallic, height, ambient occlusion) и некоторые настройки, такие как уровень шероховатости, сила затенения и другие. Текстуры пакуются в текстурные массивы (Texture2DArray) на этапе сборки. Такое отделение материалов от типов помогло сильно сэкономить память и начать переиспользовать текстуры.

Рисунок 2. Материалы вокселей и тип вокселя.

На рисунке 2 можно заметить, что тип вокселя содержит ссылки на три материала: top, bottom и side. Это необходимо для того, чтобы рисовать разные материалы на разных сторонах вокселя: недостаточно просто материала травы, так как на склонах должна проглядываться земля или глина, а под землей - текстура земли.

1.2 Шейдер

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

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

Рисунок 3. Функция сэмплинга вокселя.

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

Для плоскостей XY и ZY будет соответствовать материал Side. Для плоскости XZ необходимо посмотреть на знак компонента Y нормали поверхности. Если знак положительный, то берем материал Top, в ином случае - Bottom. Таким образом получилось не увеличивать количество сэмплов.


2. Генерация типов вокселей

Выбор типов вокселей мало отличается от генерации поверхности ландшафта: все тот же шум, возможно, немного попроще. Типы вокселей определяются на этапе генерации SDF на стороне GPU, так что, на CPU воксель имеет уже полный набор данных.

2.1 Первый слой

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

Рисунок 4. Базовая трава.
Первый слой должен располагаться таким образом, чтобы создать эффект градиента: так как это базовая трава, то она должна располагаться в основном в низинах и на склонах.

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

Рисунок 5. Маска для первого слоя.

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

Рисунок 6. Маска первого слоя с учетом склонов.

Если визуализировать маску на самом ландшафте, то можно получить интересный градиент (рисунок 7).

Рисунок 7. Маска первого слоя, визуализированная на ландшафте.

2.2 Второй слой

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

Рисунок 8. Светлая трава.
Рисунок 9. Маска для второго слоя.

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

Рисунок 10. Маска для второго слоя, визуализированная на ландшафте.

2.3 Третий слой

Третий слой будет дополнением к первым двум: чуть более желтая трава, которая больше выгорела на солнце. Она должна подчиняться правилам второго слоя, но при этом быть в виде "участков". Это создает небольшое разнообразие.

Рисунок 11. Выгоревшая трава.

Для эффекта "участков" можно взять обычный Simplex 2D шум.

Рисунок 12. Маска для третьего слоя.

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


Заключение

А что же в результате? Как выглядит ландшафт?

Рисунок 14. Несколько типов вокселей.

Как видно на рисунке 14, выглядит он плохо. Сравните с цветами из Substance Designer!

Вот на этом моменте я остановилась, так как масочки еще можно настроить, но вот сами цвета - нет. Освещение, цвето-коррекция - их просто нет, и в итоге ландшафт выглядит правда плохо.

Что же делать? Настроить освещение и пост-процессинг, чтобы еще на этапе генерации текстур в Adobe Substance Designer понимать как будет выглядеть ландшафт!

Следующий этап: освещение и постпроцессинг.