Создание следов от выстрелов на персонажах в Unreal Engine 4

Как сэкономить ресурсы системы.

Том Луман из Epic Games в своём блоге рассказал, как сделать так, чтобы на персонажах в Unreal Engine 4 оставались следы от выстрелов. Метод накладывает некоторые ограничения, зато не использует много ресурсов системы. Мы выбрали главное из руководства.

Создание следов от выстрелов на персонажах в Unreal Engine 4

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

Я заметил это скольжение в PlayerUnknown’s Battlerounds, где на персонажах использовались традиционные декали. Однако для игр с видом от третьего лица предпочтительно использование более стабильного решения.

Мне захотелось решить эту проблему для персонажей в нашей игре. Я вдохновился демо Райана Брука с GDC.

Его подход не укладывался в наш бюджет. Он требовал две целевые точки рендера (для каждого персонажа на сцене), меш с уникальной UV («манекен» из UE4, например, не имеет уникальной UV и требует дополнительных модификаций за пределами движка), а также сказывался на производительности из-за двух запросов.

Первый рендер происходит тогда, когда вам надо показать брызги на персонаже. С помощью SphereMask мы ищем пиксель, который был «поражён» выстрелом. Однако материал «не знает», где находится персонаж относительно позиции «поражения», поэтому каждому пикселю анимированного персонажа задают позицию в мире (world position) во вторую целевую точку рендера.

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

Есть способ оптимизировать технику, используя узел pre-skinned local position. Он заменяет позицию в мире, которую мы запекли в целевую точку рендера на заранее созданную локальную позицию. Я сделал быстрый чертёж и модифицированную версию материала Райана, «захватил» сцену и перевёл RT в статичную текстуру. Таким образом, нужда в повторном рендере отпадает. Каждый «выстрел» мы трансформируем локацию удара в pre-skinned local-space меша, до того, как она будет применена к персонажу.

Оптимизация сделала разработку дешевле, но меня всё равно не устраивало то, что при каждом ударе использовалась требовательная операция DrawMaterialToRenderTarget.

Я «открепил» все целевые точки рендера, чтобы попробовать использовать лишь SphereMasks для создания эффекта. Благодаря ему, количество «ударов» будет лимитировано. Я решил, что 3-5 ударов будет достаточно, так как следующий выстрел гарантировано убьёт противника (если речь идёт не о боссах).

Как и в случае с RT-эффектами, для работы маски с анимированным мешем нам нужна была эталонная поза (reference pose), на которую бы и накладывалась маска. Когда персонаж получает удар, мы трансформируем его позицию в мире в позицию позы (с помощью информации о BoneName). Вы можете сделать это инвертировав трансформацию локации удара с текущего преобразования кости, а затем преобразовав эту локацию с помощью reference pose transform той же кости.

Создание следов от выстрелов на персонажах в Unreal Engine 4

Здесь можно увидеть визуализацию преобразования, принятого к локации удара (отмечено зелёным цветом). Синие линии отмечают трансформации эталонной позы. Фиолетовые позволяют увидеть разницу.

Создание следов от выстрелов на персонажах в Unreal Engine 4

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

Я преобразовал оригинальные локации ударов в локацию эталонной позы. Я получил постоянную позицию, которая не анимировалась. Эту локацию мы включили в шейдер, который не использовал pre-skinned позиции для маски. Для поддержки нескольких ударов мы изменяли имя параметра с каждым новым ударом: например, HitLocation_1, HitLocation_2 и так далее.

Ещё многое можно улучшить, но основная техника работает хорошо. В своих примерах я добавил HightLerp, чтобы получить более интересный визуальный эффект, чем от простой SphereMask.

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

4444
7 комментариев

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

10
Ответить

Мне кажется тогда всё это было возможным, как раз из-за «скудности» графики. Сейчас, множество прототипов упирается в uncanny valley. Создать расчлененку можно, но выглядеть в большинстве случаев она будет не так убедительно, как в те времена, а порой - даже смешно.

1
Ответить

в новом Resident Evil 2, очень неплохо смотрятся повреждения от выстрелов на зомби: пробитый череп, порванная одежда и плоть и отстрел конечностей в целом

2
Ответить

Неужели! Статья про разработку на ДТФ.
Метод интересный, особенно для мелких проектов. Спасибо!

1
Ответить

Кстати, интересно было бы прочитать статейку про SOF.

Ответить

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

Ответить