Песнь без пламени: создание материала льда на Unreal Engine 4

От референсов до оптимизации.

Технический художник Тайзид Корамбайл (Taizyd Korambayil), работавший в Epic Games, рассказал порталу 80.lv о материалах и настройке реалистичного льда в UE4.

DTF публикует перевод статьи.

В 2015 году я выпустился из Колледжа искусств и дизайна Саванны со степенью бакалавра геймдизайна. С 2015 года работал в Epic Games в команде Engine Content, в основном — руководил исправлением багов и обновлением шаблонных проектов, поставляемых с движком. Мне также довелось немного поработать над Paragon (исправляя баги с артами на картах).

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

Редактор материалов Unreal Engine 4

С помощью этого инструмента можно сделать практически что угодно. Конечно, бывают исключительные случаи, когда нужно удалить custom-ноды и использовать HLSL, но чаще всего его возможностей хватает. В свою очередь, Substance Designer больше похож на процедурный Photoshop.

Я часто пользуюсь и тем, и другим: обычно генерирую паттерны для шейдеров в Substance Designer и переношу их в Unreal Engine. Не думаю, что их можно сравнивать — они выполняют разные функции.

Референсы

Я быстро понял, что могу сделать много видов льда — замёрзшие камни, ледяные кубы, поверхность озера — и каждый будет особенным. Было решено создать универсальный шейдер с настраиваемыми параметрами, которые можно легко подогнать под любой из вариантов. Я разглядывал референсы, служившие источником вдохновения — замёрзшие озёра, арктические пейзажи, ледяные пещеры. Мне хотелось лучше понять, как разработчики делают в Unreal материалы.

Цвета

Я решил взять аналоговую цветовую палитру оттенков синего и использовал Adobe Kuler. Цвета смешиваются при помощи масок. Обычно я беру текстуры с паттернами, в редакторе материалов делаю их разнообразнее и «глубже», а потом использую для интерполяции цветов. Результат зависит от множества факторов — непрозрачности, внутренних элементов (subsurface), ракурса и так далее. Думаю, цвета в этот момент могут быть слишком насыщенными — потом художник может откорректировать их, как хочет.

Паттерны

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

Узор используется в шейдере несколько раз — для маски пузырьков, шероховатостей в отдельных зонах, и выступающей части на нижнем слое — трещинах под поверхностью льда.

Освещение

Определённо, крайне важная часть, особенно для материалов, в которых есть подповерхностные и полупрозрачные элементы. На скриншотах я использовал карту Epic Lightstage — она хорошо подходит для демонстрации ассетов и в базовом варианте, но я слегка подстроил её под свои нужды. Была задана основная система освещения с тремя источниками, мощной подсветкой (rim/backlight), выхватывающей подповерхностные части, и лёгкой цветокоррекцией для создания холодной атмосферы.

Глубина

Одной из моих задач была имитация «глубины» модели — я очень хотел уловить это свойство материала. В Unreal есть нод BumpOffset, позволяющий добавить текстурам параллакс-эффект. В итоге я накладывал друг на друга текстуры с отличающимися значениями параллакса и потом совмещал их.

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

Предпросмотр материала

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

Текстуры

В материале используется 4-5 текстур. Одна из них служит для «запекания» карты нормалей меша, три других — паттерны в каждом канале, и ещё одна — карта шума нормалей общего назначения. Для генерации узоров я использовал Substance Designer (один или два — нагуглил). На изображениях ниже видно, как текстуры совмещаются.

NormalMap

BumpOffset Node

Этот нод обычно используют для имитирования глубины материала с помощью эффекта параллакса. Вы вводите значение координат (сoordinate value), высоты и отношение высоты (height ratio value). Значение высоты определяет основание или плоскость, относительно которой она меняется. Если установить высоту на 0 и отношение высоты на -1.0, пиксели будут отрисовываться как бы под поверхностью. Если же установить height ratio value на 1.0 — они будут выпирать над поверхностью (что будет выглядеть очень плохо, если задавать не минимальные значения). Есть инструкция по применению нода.

Создание масок для раскраски поверхности

На этом шаге часть созданных текстур смешивается и совмещается в маски — их мы используем для контроля за цветами шейдера. Корректируя описанные ниже параметры, можно менять результат, делая копии (Instances).

Сначала мы создаём скалярный параметр «Master Tiling» — от него будет зависить тайлинг всего материала.

Далее создаём базовый набор настроек, которые будут повторяться для всех текстур, используемых в шейдере. Назначаем скалярный параметр контролировать тайлинг для каждого TextureSample и размножаем его с помощью параметра Global Tiling. Также мы настраиваем нод bumpOffset и его значения.

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

Пузырьки, трещины, маски поверхности

На этом графике показано создание масок поверхности, задающих зоны, в которых пузырьки становятся видимыми.

На изображении выше мы повторяем процесс для каждого текстурного семпла.

1) Самый верхний слой пузырьков. Применяем их паттерн к каналу текстуры Blue, устанавливаем height ratio value на -0.2, и они появляются прямо под поверхностью.

2) Пузырьки, которые находятся глубже под поверхностью. Сначала мы создаём два скалярных параметра и добавляем их к координатам текстур — это позволит добавлять оффсеты в слои U и V, и результат станет разнообразнее. Затем мы повторяем процедуру и вновь задаём значение тайлинга. Оно должно быть выше, тогда пузырьки будут меньше, что усилит эффект фальшивой глубины. Соотношение высот в ноде BumpOffset устанавливаем в -1.0.

Итоговый вариант этой сетки нужно размножить с инвертированной Mask_01 из предыдущего раздела. Тогда пузырьки будут не видны, если они окажутся в непрозрачной части поверхности. На гифке ниже можно увидеть результат.

Шероховатости материала

Этот раздел графа отвечает за добавление шероховатости (roughness input) материала. Мы повторяем то, что уже делали, и задаём параметры тайлинга с пятнистым паттерном на канале Blue, используя его как маску линейной интерполяции между минимальными и максимальными значениями неровности. Эти значения выставляются как параметры, чтобы их можно было корректировать. Наконец, мы делаем финальную линейную интерполяцию с маской-паттерном трещин из Mask_01 — так у потрескавшихся областей будет иная степень неровности, нежели у остальной поверхности.

Этот граф будет служить roughness-выходом материала. На предпросмотре это будет выглядеть так:

Непрозрачные и подповерхностные маски

В следующем разделе мы настроим отображение непрозрачности материала. Эта часть графа влияет на материал следующими двумя способами:

  • ​Определяет, насколько сильно рассеивается свет в меше по удалённости от поверхности.
  • Со включённой светопроницаемостью (translucency) — определяет, насколько материал прозрачен.

Итак, от него зависит, как отображается непрозрачность в зависимости от включения или выключения светопроницаемости.

(Важно: на отрисовку светопроницаемой версии материала уходит много ресурсов, используйте её с умом. Идеально, если все объекты, использующие этот шейдер, можно будет быстро заменить на упрощённые версии при необходимости).

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

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

Первая часть — просто базовая маска с текстурами, созданными нами раньше, но с добавлением Френеля — так грани объекта не становятся абсолютно невидимы (иначе будет казаться, что он «парит» над землёй).

Включив предпросмотр opacity-нода, вы увидите следующее:

Маска зоны видимости камеры

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

На этой гифке видно, что делает сеть. Маска становится тем темнее (то есть прозрачнее), чем ближе камера к объекту.

Финальный блендинг маски непрозрачности

На этом этапе мы совмещаем все предыдущие маски, а результат отправляется в Opacity Input материала.

На предпросмотре можно увидеть нечто подобное:

BaseColor Pass

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

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

На предпросмотре нода FuzzyShading вы увидите что-то вроде этого. Выход FuzzyShadingNode будет вести во вход BaseColor материала. Этот результат мы используем при создании подповерхностного цвета в следующем разделе.

Подповерхностный цвет

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

Карта нормалей

Настроить нормали материала просто — используется текстура нормали с UV-координатами из раздела Surface Masks. Также добавляется статичный переключатель параметра на случай, если мы заходим скомбинировать её с картой нормалей «запечённого» объекта. Эта опция сделана таким образом, что при применении материала к объекту с запечённой картой нормалей, нормали льда смешиваются с нормалями объекта.

Производительность, оптимизация

При создании копии (instance) этого материала у вас есть выбор — использовать её как светопроницаемый-подповерхностный материал или как непрозрачный. Светопроницаемость требует много ресурсов, и в идеале у вас должна быть возможность переключиться на материал попроще. Непрозрачная версия не так впечатляет, зато куда производительнее.

Если вы сравните сложность этих двух версий материала, вы заметите серьёзные отличия. Для правильной оценки производительности, разумеется, следует использовать GPU-профайлер, с которым вы можете получить точную информацию о том, что происходит в сцене.

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

#арт #опыт

 
Источник: DTF

Читайте также