TOTAL RELOAD : процедурная генерация проводов. (часть 2)
Это продолжение статьи о том как работают провода в TOTAL RELOAD.
Предупреждение: эта статья не является инструкцией, также, скорее всего, она не будет полезна профессиональным программистам, которые имеют опыт работы с процедурной генерацией мешей. Статья содержит картинки, видео и обобщенные объяснения разработанный нами системы генерации проводов.
SSAO тени и провода
При разработке проводов, которые поддерживают тесселяцию, мы столкнулись с проблемой: SSAO из Post Processing V2 работал неверно с проводами в режиме Forward. В результате этого все провода имели неверный эффект затемнения. Если задать произвольный “RenderType”, то провода выглядели примерно так:
Так же провода имели проблемы здесь:
Нужно было добиться такого результата:
Все это из-за того, что SSAO, при расчете затемнения, использует _CameraDepthNormalsTexture - текстуру. Провод использовал встроенный RenderType (или использовал наш, который не переопределен) и в результате _CameraDepthNormalsTexture выглядела так (провода здесь нет):
_CameraDepthNormalsTexture - текстура формируется “Internal-DepthNormalsTexture”-шейдером. Чтобы все работало как надо, для режима Forward этот шейдер придется переопределить. Провод после переопределения:
Но это еще не все! Переопределить встроенный шейдер нужно так, чтобы он учитывал тесселяцию. Иначе при расчете карты нормалей будет использована низкополигональная модель провода и тени будут выглядеть криво. Карта нормалей, сформированная без учета тесселяции:
Так «гладко» и красиво выглядит финальный вариант:
Он же без тесселяции (для сравнения):
Главное, при расчетах, меньше ошибаться. Иначе можно получать вот такие вот макароны-спиральки:
Эффект виртуальности мира
Игровой мир является частично виртуальным и мы всячески подчеркиваем это. Провода - это одна из вещей, которая будет намекать игроку о особенности мира. Для этого было решено показать виртуальность мира через небольшую дезинтеграцию проводов в области их пересечения с игроком или другими предметами, которые наделены энергией.
Итого: провод делится на равные сегменты, которые заносятся в 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 работают так:
А здесь провод дезинтегрировался (внимание на тень):
Внутренность провода
Результат работы над внутренностью тонкого провода выглядит вот так:
Дематериализованный толстый провод:
Как это работает
Провод создается на основе сплайна:
- берутся необходимые модели (части провода)
- изгибаются вдоль сплайна
- финальная модель запекается в “Asset” и используется в игре
Так же сплайн, на основе которого был построен провод, используется для отрисовки внутренней части провода:
- определяется ближайший подвижный объект и его проекция на сплайн
- строится изогнутый вдоль сплайна участок дематериализованных “кубиков”
Если оболочка провода запекается один раз и алгоритм запекания не требует оптимизации, то внутренняя часть провода (дематериализованные кубики) требует оптимизацию. Изгибание модели кубиков вдоль сплайна на процессоре - это тяжелая вычислительная задача:
Было принято решение: гнуть кубики вдоль сплайна на GPU. После этого нагрузка на процессор составила почти 0!
Как быстро “гнуть кубики” на GPU
Основная идея подхода была подсмотрена здесь: https://medium.com/@roy.theunissen/gpu-spline-deformation-in-unity-a710f55f210c
Если коротко:
- печем параметры (position, rotation, scale) участков сплайна (с эмпирически подобранным шагом N) в текстуру
- декодируем матрицы на видеокарте и смещаем вершины
- радуемся результату
Оптимизация и поддержка слабеньких видеокарт
Когда вы счастливый обладатель хорошей видеокарты, а именно: ваша видеокарта поддерживает тесселяцию, то все замечательно! Провод будет выглядеть высокополигональным и невидимые части кубиков дематериализации будут качественно скрыты внутри провода (в местах изгиба провода).
А что, если вы не такой уж и счастливый обладатель видеокарты и имеете что-то относительно неновое? Тогда ваш провод во-первых будет смотреться как низкополигональный параллелепипед, а во-вторых он будет проявлять (в областях изгиба) кубики дематериализации. Например, этот изгиб проявит кубики дематериализации так как оболочка провода сильно отступает от создающего его сплайна:
А если еще сильнее низкополигонизировать участки провода (зеленая линия имитирует более низкополигональную версию), то будет ужас. Очевидно, что в местах сильного изгиба провода кубики всегда будут предательски торчать:
Решение: просчитать тангенсы и на основе отклонений подменять участки проводов на более/менее полигональные. Просто же, правда?:) Визуализация тангенсов:
В результате создание модели провода работает так: в зависимости от кривизны участка, выбирается соответствующая модель провода (высокой/низкой или средней полигональности). В результате, данный провод способен скрывать (в местах изгиба) внутренние кубики даже на слабых видеокартах.
Модели разной полигональности, которые используются при построении провода:
Видео примера настройки провода:
На этом пока все, ссылки на нас:
TORSHOCK.COM / VK / DTF / Twitter / FB
Спасибо за материал. Вот бы на ДТФ подобного
Спасибо :)