TOTAL RELOAD : процедурная генерация проводов. (часть 2)

Это продолжение статьи о том как работают провода в TOTAL RELOAD.

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

SSAO тени и провода

При разработке проводов, которые поддерживают тесселяцию, мы столкнулись с проблемой: SSAO из Post Processing V2 работал неверно с проводами в режиме Forward. В результате этого все провода имели неверный эффект затемнения. Если задать произвольный “RenderType”, то провода выглядели примерно так:

TOTAL RELOAD : процедурная генерация проводов. (часть 2)

Так же провода имели проблемы здесь:

TOTAL RELOAD : процедурная генерация проводов. (часть 2)

Нужно было добиться такого результата:

TOTAL RELOAD : процедурная генерация проводов. (часть 2)

Все это из-за того, что SSAO, при расчете затемнения, использует _CameraDepthNormalsTexture - текстуру. Провод использовал встроенный RenderType (или использовал наш, который не переопределен) и в результате _CameraDepthNormalsTexture выглядела так (провода здесь нет):

TOTAL RELOAD : процедурная генерация проводов. (часть 2)

_CameraDepthNormalsTexture - текстура формируется “Internal-DepthNormalsTexture”-шейдером. Чтобы все работало как надо, для режима Forward этот шейдер придется переопределить. Провод после переопределения:

TOTAL RELOAD : процедурная генерация проводов. (часть 2)

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

TOTAL RELOAD : процедурная генерация проводов. (часть 2)
TOTAL RELOAD : процедурная генерация проводов. (часть 2)

Так «гладко» и красиво выглядит финальный вариант:

TOTAL RELOAD : процедурная генерация проводов. (часть 2)

Он же без тесселяции (для сравнения):

TOTAL RELOAD : процедурная генерация проводов. (часть 2)

Главное, при расчетах, меньше ошибаться. Иначе можно получать вот такие вот макароны-спиральки:

TOTAL RELOAD : процедурная генерация проводов. (часть 2)

Эффект виртуальности мира

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

Итого: провод делится на равные сегменты, которые заносятся в Octree.

В области пересечения провода и игрока в шейдере рисуется отверстие:

for(int k = 0; k < _OBJ_COUNT; ++k) {

half dist = length(_ObjPos[k].rgb-i.worldPos);

half sphere = 1 - ( (dist - _GLOBALMaskRadius) / _GLOBALMaskSoftness);

float a = sphere - 0.1;

clip(a);

albedo.rgb += MUL_COL * step(a, 0.1);

}

dist - дистанция от пикселя до объекта

Далее: если дистанция меньше определенного радиуса, то делается ‘clip’. Кроме этого по периметру clip рисуется подсветка.

dist - the distance from the pixel to the object

Также нужно рисовать отверстие и при формировании _CameraDepthNormalsTexture. Иначе SSAO будет неверно затемнять провод.

Кроме этого нужно рисовать отверстие и при формировании _CameraDepthTexture. Иначе тени будут рисоваться там, где провод дематериализовался.

В результате тени и SSAO работают так:

TOTAL RELOAD : процедурная генерация проводов. (часть 2)

А здесь провод дезинтегрировался (внимание на тень):

TOTAL RELOAD : процедурная генерация проводов. (часть 2)
TOTAL RELOAD : процедурная генерация проводов. (часть 2)

Внутренность провода

Результат работы над внутренностью тонкого провода выглядит вот так:

TOTAL RELOAD : процедурная генерация проводов. (часть 2)

Дематериализованный толстый провод:

TOTAL RELOAD : процедурная генерация проводов. (часть 2)

Как это работает

Провод создается на основе сплайна:

  • берутся необходимые модели (части провода)
  • изгибаются вдоль сплайна
  • финальная модель запекается в “Asset” и используется в игре

Так же сплайн, на основе которого был построен провод, используется для отрисовки внутренней части провода:

  • определяется ближайший подвижный объект и его проекция на сплайн
  • строится изогнутый вдоль сплайна участок дематериализованных “кубиков”

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

TOTAL RELOAD : процедурная генерация проводов. (часть 2)
TOTAL RELOAD : процедурная генерация проводов. (часть 2)

Было принято решение: гнуть кубики вдоль сплайна на GPU. После этого нагрузка на процессор составила почти 0!

Как быстро “гнуть кубики” на GPU

Основная идея подхода была подсмотрена здесь: https://medium.com/@roy.theunissen/gpu-spline-deformation-in-unity-a710f55f210c

Если коротко:

  • печем параметры (position, rotation, scale) участков сплайна (с эмпирически подобранным шагом N) в текстуру
  • декодируем матрицы на видеокарте и смещаем вершины
  • радуемся результату

Оптимизация и поддержка слабеньких видеокарт

Когда вы счастливый обладатель хорошей видеокарты, а именно: ваша видеокарта поддерживает тесселяцию, то все замечательно! Провод будет выглядеть высокополигональным и невидимые части кубиков дематериализации будут качественно скрыты внутри провода (в местах изгиба провода).

А что, если вы не такой уж и счастливый обладатель видеокарты и имеете что-то относительно неновое? Тогда ваш провод во-первых будет смотреться как низкополигональный параллелепипед, а во-вторых он будет проявлять (в областях изгиба) кубики дематериализации. Например, этот изгиб проявит кубики дематериализации так как оболочка провода сильно отступает от создающего его сплайна:

TOTAL RELOAD : процедурная генерация проводов. (часть 2)

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

TOTAL RELOAD : процедурная генерация проводов. (часть 2)
TOTAL RELOAD : процедурная генерация проводов. (часть 2)

Решение: просчитать тангенсы и на основе отклонений подменять участки проводов на более/менее полигональные. Просто же, правда?:) Визуализация тангенсов:

TOTAL RELOAD : процедурная генерация проводов. (часть 2)

В результате создание модели провода работает так: в зависимости от кривизны участка, выбирается соответствующая модель провода (высокой/низкой или средней полигональности). В результате, данный провод способен скрывать (в местах изгиба) внутренние кубики даже на слабых видеокартах.

Модели разной полигональности, которые используются при построении провода:

TOTAL RELOAD : процедурная генерация проводов. (часть 2)

Видео примера настройки провода:

На этом пока все, ссылки на нас:

1414
2 комментария

Спасибо за материал. Вот бы на ДТФ подобного

2

Спасибо :)