14/08/2019

Шейдер для эффекта возгорания

Меня зовут Таня Жеглова, хочу поделиться эффектом, который сделала для игры Hydropunk.

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

Задача оказалась не простая, так что первым делом были заданы вопросы на форумах, и в том числе моему сенсею — @balaganvfx. Он-то и скинул мне главный референс для вдохновения.

В этой статье сделаем акцент именно на шейдер, так как в нем происходит главная магия.

1. Создаем параметр, который будет хранить данные
о местоположении искры — Material Collection

Чтобы газ начинал гореть именно в той точке, где в него попала искра, нужно получить в настройках материала (Material editor) местонахождение искры.

Искра представляет собой блюпринт, который перемещается по уровню. Для хранения информации о местонахождении искры в каждый конкретный момент мы будем использовать Material Collection. Это специальный параметр, который хранит местоположение объекта и который можно использовать в настройках материала.

Первое, и самое главное, что нужно сделать для этого эффекта — создать Material Collection. Для этого кликните ПКМ по контент браузеру — выберете Material & Textures -> Material Parameter Collection.

В нашем случае, надо получить точку в пространстве, а значит — вектор (x,y,z). Открыв Material Parameter Collection, видим два параметра, которые можно создать — скалярное число и вектор. Нам нужен вектор. Кликнем по плюсу (Adds Element). В слоте Parameter Name введем уникальное имя нашего параметра. В моем примере — TexLoc1. Сохранимся.

2. Создаем актор искры в блюпринте

Теперь создадим обычный актор для искры. В event graph создаем такую конструкцию:

В слоте Collection из выпадающего списка выберем только что созданный material collection, а в parameter name укажем уникальное имя параметра — TexLoc1.

Каждый кадр этот блюпринт обращается к своему местоположению в пространстве (вектор x,y,z) и передает это значение в наш Material Parameter Collection. Компилируем, сохраняем. Переходим к самой интересной части.

3. Создаем шейдер горения

3.1. Определяем местоположение искры

В первую очередь, нужно определить местоположение искры. От нее пойдет горение материала. Для этого кликнем ПКМ по графу в Material Editor и вызовем ноду Collection Parameter. В панели details выберем из списка наш параметр, а в графе name так же укажем имя TexLoc1. Теперь внутри материала мы имеем параметр, который хранит в себе местоположение актора искры.

3.2. Создаем логику взаимодействия искры и материала

Параметр TexLoc1 — точка, которая имеет подвижные координаты. Absolute World Position — наоборот, блокирует положение материала в абсолютной точке. Distance — то, что указывает дистанцию между TexLoc1 и Absolute World Position.

3.3. Создаем маску воспламенения

После того, как мы разобрались с положением материала и актора искры в пространстве, зададим эффект горения. Для этого создадим скалярный параметр (горячая клавиша S+ЛКМ) и дадим ему имя, в моем примере — MaskRadius_ignition. Этот параметр является радиальной дистанцией между актором искры и материалом в мировом пространстве. Параметром Power мы регулируем градиент, т.е. мягкость маски. Нода Subtract вычитает из градиента текстуру, благодаря чему мы получаем "рваные" края. 

Если вы имитируете огонь, текстуру лучше взять огненную. Текстура, само собой, должна быть бесшовной и желательно иметь степень двойки (1024*1024, 512*512 и т.п.)

Хорошим тоном будет использование Clamp, чтобы все математические операции не выходили за пределы от 0 до 1.

Для проверки того, что получилось — подключим конструкцию в Emissive. Черный цвет — дистанция MaskRadius_ignition от актора. Белый — материал, который использует мировые координаты.

Чтобы посмотреть, как это работает в динамике, без подключения к блюпринтам, подключим такую конструкцию:

Связка Time frac изменяет скалярное число от 0 до 1, там самым анимируя значение MaskRadius_ignition.

Основная логика готова, займемся красотой.

3.4. Создаем основной материал для газа

Для проверки у нас был подключен белый цвет (1,1,1) в lerp. Заменим цвет на материал, который хотим использовать. В моем примере я сделала ядовитый газ, который состоит из двигающегося шума и маски. Вот как это выглядит (текстура состоит из 4 каналов. В R канале — шум, в A канале — маска):

Теперь наш материал исчезает по маске.

3.5. Добавляем цвет

Lerp смешивает цвет огня и цвет газа по альфе маски воспламенения. Где был 0, т.е. черный цвет — там стал цвет огня. Где был 1, т.е. белый цвет, там стал цвет газа. В пин Opacity подается такая же маска, поэтому цвет огня должен полностью стать прозрачным, но так как мы используем Power, который создает мягкий градиент между черным и белым, получается полоска огня. Чем ниже значение Power, тем мягче переход и шире полоска. 

3.6. Добавляем дымный след

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

Сейчас Маска дыма равна нулю, поэтому мы видим анимацию только для радиуса горения.

Добавим проверочную связку Time frac и сделаем радиус маски чуть меньше.

Материал готов к использованию! 

3.7. Подготваливаем материал к использованию в блюпринте

Теперь отключаем проверочные конструкции с Time Frac и ставим значения для параметров в 0, которые теперь будем менять из блюпринта.

3.8. Создаем партикловую систему

Для проверки того, как работает этот материал, вы можете создать дефолтную партикловую систему. Для этого кликните ПКМ в контент браузере — Create Particle System. В ноде Required назначите созданный материал.

4. Создаем блюпринт горения

В компоненты добавим систему партиклов с нашим материалом. Включать конструкцию будем по кнопке (убедитесь, что в настройках актора указан Player 0).

Чтобы изменять параметры радиуса, вытащим компонент — партикловую систему — в евент граф. Из компонента вытащим связь и найдем Create Dynamic Material Instance. Выберете материал, который создали, а если ваша партикловая система содержит несколько материалов, укажите индекс материала (его можно увидеть в details компонента партикловой системы).

Так как изменять радиус мы будем во времени, добавим компонент — Timeline. Внутри таймлайна создадим 2 Float графика — для горения и для дыма (они будут немного разные), но логика одна — в начале радиус = 0, в конце — ваш размер. Чем больше размер Float числа и короче Length таймлайна, тем выше скорость вашего эффекта.

Наконец, вытаскиваем из Dynamic material Instance return Value и заводим Set scalar Parameter Value, а в Parameter name указываем имя параметра возгорания (в моем случае — MaskRadius_ignition). Повторяем то же самое для Дыма.

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

Не забываем уничтожать актор после того, как он отработал — ветку finished направляем в Destroy Actor.

Итого

Вот так эффект выглядит непосредственно в игре. Облако газа:

Взрыв газа:

Надеюсь, этот гайд был полезен!

Если у вас есть вопросы или предложения — пишите мне на почту 

Если вам интересно узнать больше об игре — следите за нашей страницей на конкурсе у Digusting Men.

Recent Posts

ООО или ИП? Зачем, когда, как и почему?

20/08/2019
Дела юридические на территории СНГ весьма плачевны, когда дело касается инди — запускаем цикл статей, который содержит общие и не особо положения.

Видеопродакшен в Unreal Engine 4

30/07/2019
Что скрывается за быстрым рендером, можно ли использовать игровые движки в видеопайплайне и к чему нужно готовиться при работе с движком.

Как в НИУ ВШЭ воспитывают новых Кодзим

11/07/2019
Реально ли попасть в геймдев, отучившись 4 года и получив диплом? Чему предстоит обучаться? Какие подводные камни? Что говорят студенты и преподаватели?