[Перевод] Стильный водопад из RiME в игровом движке: делаем водяной поток

Это вторая (и последняя) часть гайда по созданию водопада в Unity или UE4 после вдохновения игрой RiME. В первой разобрались с инструментами, выбрали среду разработки и создали шейдер кругов на воде. Почему начали именно с этого? Все просто: там используется большинство методов, которые сейчас понадобятся при создании водопада. Но тут есть и свои хинты. Тянуть не будем — давайте под кат.

qrmnw2qdz0twpkledjq7krzgvpe.gif
Начнем с очевидного — берем обычный Panner и меш. Вы, возможно, удивитесь: зачем так много полигонов? Как и в водопаде Саймона, я использую vertex displacement для смещения вершин модели в 3D-пространстве через шейдер. А чтобы это выглядело красиво, нам понадобятся дополнительные вершины. Для оптимизации можно создать LOD-меши (Level Of Detail) с меньшим количеством полигонов, и заставить Unity переключаться на модели с более низким разрешением.

ywpzcfxo4ezux9s55szmqtmk3my.jpeg

UV-шов расположен сзади. Даже если у водопада нет видимых швов текстуры, все равно лучше подстраховаться и поместить UV-швы в наименее видимые для игрока места.

Как и в случае меша кругов на воде, UV-развертка должна быть с наименьшим количеством швов на модели при использовании тайлинговых текстур. Одна сторона UV shell привязана к 0 по направлению U, а другая к 1. Помните, что по ширине UV shell должен укладываться в целое значение U-пространства (например, тайлинг 1, 2 или 3). Если мы будет тайлить 1,2 раза в направлении U, то на стыке появится шов. А нам нужна бесшовная тайлинговая текстура.

fegfwibej9wk6dhs734bvctsa3a.jpeg

Так выглядит карта нормалей тайлингованная 3 раза по U-направлению. Заметьте, как правая часть 3D-изображения идеально попадает в 1 по U-направлению. Установка тайлинга равным 3 по направлению U означает, что текстура повторяется 3 раза между 0 и 1 в направлении U (на изображении ниже показано UV-пространство от 0 до 1).

2xz2s5csjdsxlfsay62vvrmggnq.png

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

bdkkmd5spr83l7ux538khbbrmhe.jpeg

Чтобы картина была более полной, посмотрите на гифку, где я перемещаю UV-карту. У одной ширина UV shell составляет ровно 1 (прекрасно тайлится), другая урезана так, чтобы был виден шов. Поскольку мы используем только тайлинговые текстуры, UV-карта не обязательно должна быть в пределах от 0 до 1.

36ig0twz0bd8ln0c5b-suk7ud7g.gif

У водопада (как и с кругами на воде) я тоже исказил UV-карту. Поэтому при использовании Panner текстура движется быстрее или медленнее в определенных точках меша. Дополнительные полигоны в этом случае делают переход между искаженными участками менее заметным. Попробуйте включить soft selection для искривления UV — так получаются более плавные переходы.

nbu_6ry4rio5yhgndqrhelxif6a.gif

Обратите внимание, как текстура сильнее растягивается на изгибе водопада — этот эффект можно по-разному регулировать. Всегда представляйте себе переход с UV на 3D. Чем ближе друг к другу вершины UV, тем быстрее текстура будет перемещаться по этим UV в 3D-пространстве при использовании Panner. Если переместить UV-вершины дальше друг от друга, текстура будет появляться чаще на этой области меша и медленнее перемещаться в 3D-пространстве при использовании Panner. Экспериментируйте, пока не получите желаемый эффект.

gz-wmzgewq48wkyvg7djsce4mti.gif
Примечание: это новый материал. Создайте новый материал и нанесите на него новый шейдер (смотрите часть 1).

Мы снова используем одну текстуру с несколькими вариантами скорости/направления Panner, также как и тайлинга UV (флипнем один из них, придав отрицательное значение U). Добавим их друг на друга, чтобы получить ощущение более случайного эффекта. И используем их аутпут для добавления множества других настроек: opacity, color variation и vertex displacement.

qcksdynqdbihlyfhrx0pqbw6ihq.gif

Также я использовал прокрутку карты нормали. Только одну, потому что она так быстро движется с двумя Panner для варьирования, что разницы в данном случае не будет. Умножим его на значение синего равным 1 (на гифке указано 255 — это то, когда у каждого канала есть 256 шагов, от 0 до 255), а также на значения красного и зеленого равными 60 (или значение 0,23, если вы используете шкалу от 0 до 1). В результате слишком насыщенная карта нормалей стала более ровной и спокойной. Чтобы вернуть более интенсивные нормали — сохраняйте одинаковыми значения R и G и увеличивайте их. Так вы получите чуть больше настраиваемости внутри самого шейдера.

lxi7chekbzb8xyvxpdqkmgmucaa.gif

Мы настроили меш, UV и другие компоненты, которые понадобятся для работы водопада. Давайте проанализируем несколько вещей на этой гифке:

qapzzf8nruvfjb_pe9nzaoknb6s.gif

На ней поток воды прокручивается по 3D-мешу и разрывается внизу. Видно, что здесь используется тип прозрачного кат-аут рендеринга — водопад полностью непрозрачный или полностью прозрачный. Вода меняет цвет, когда приближается к земле, и выглядит менее гладкой на белых участках. Тут же заметен и vertex displacement, о котором мы говорили ранее. Все эти факторы придают водопаду более живой и натуральный вид. Теперь подробнее — начнем с цвета.

Я использую Lerp (линейная интерполяция) с двумя цветами и прокручивающуюся черно-белую текстуру, которую сделали в начале, в качестве альфы (входного сигнала). Эта текстура в основном служит маской для выбора — показывать синий или белый цвета. А Add подключается к vertex color node (ноду с цветами вершин).

bkzjmiql_sfs8k92wnkp9c_lc2a.gif

Ниже показаны использованные vertex colors. Они работают подобно vertex colors из первой части — то есть как градиент. Цвета нужны, чтобы убедиться, что вода становится белее к нижней части водопада. Поскольку мы добавляем vertex colors поверх уже имеющегося цвета (самая белая точка vertex color = 1), можно легко превысить значение 1. Если это произойдет, то мы получим слишком яркие и перенасыщенные результаты. Поэтому в конце стоит Clamp, чтобы максимальные значения не превышали 1 (у Clamp минимальное = 0, а максимальное = 1). Использование белого и синего цветов гарантирует, что при «разрыве» воды, она окажется в белой (вспененной) области меша и получит белую границу вокруг прозрачных областей. Чем светлее vertex color, тем больше вершин выпирают наружу. Таким образом, конец водопада, который светлее по vertex color, движется более хаотично. Изгиб водопада тоже немного светлее — отсюда его скорость.

bfr1id-0wluszfm7d50vil-4ste.jpeg

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

qrmnw2qdz0twpkledjq7krzgvpe.gif

Дополнительно подключаем fresnel в канале emission для создания фейкового света, который проходит сквозь воду. Fresnel делает пиксель настолько светлее, насколько вектор нормали на поверхности 3D-объекта отклонен от камеры и настолько темнее, насколько вектор нормали направлен в камеру (перпендикулярен камере).

Если посмотреть под углом, то водопад становится более «тонким» — здесь пригодится fresnel, чтобы показать больше фейкового света в этих зонах. При этом нельзя, чтобы водопад светился — для ослабления эффекта я использовал min node. Fresnel часто используется с шейдерами воды для изменения цвета в зависимости от угла обзора. Подключение 0 (черный) к emission ничего не даст, а подключение 1 (белый) сделает его un-lit шейдером, который будет выглядеть полностью освещенным со всех сторон (даже если он будет находиться в тени).

jjjjbh3vc-8g3wycbgbvuwvcogg.gif

Для придания гладкости я флипнул vertex color, используя one minus node. Если вы проскроллите немного выше, то увидите, что vertex color светлые снизу и темные сверху. Перевернув их (чтобы стало светло сверху и темно снизу), можно сделать нижнюю часть водопада менее гладкой по сравнению с верхней. Более темные значения равны менее гладкой поверхности, а более светлые значения — более гладкой.

1_acbbb2nxklhzl-xtbnhkrz55c.jpeg

Vertex displacement или vertex offset — важная штука, необходимая для реализации этого эффекта. Смещая вершины, водопад выглядит менее статичным и соответственно более живым.

Это водопад без vertex displacement:

mz7pkiho3saqawgcua8jrzmiq8e.gif

А вот с vertex displacement:

8dlnofaiqpdtrw3gk8gxbjvp4he.gif

Перемещая вершины на основе нашей прокручивающейся черно-белой текстуры, водопад становится плавнее. Если направить значение 0,5 в аутпут vertex offset главного нода, то ничего не случится. Постарайтесь держать серый цвет со значением 0,5 в качестве стандартного значения вершины. Все, что ниже 0,5, будет двигаться в отрицательном направлении, а все, что выше 0,5 — в положительном.

Так откуда нам знать, в каком направлении будут двигаться вершины? Что такое верх и вниз?

В нашем случае мы хотим отодвинуть вершины от поверхности меша. Отрицательное направление будет означать, что вершина движется «внутрь» относительно направления нормали вертекса на поверхности, а положительное направление будет двигаться «наружу». Для именно такого движения вершин мы можем использовать vertex normals. Каждая вершина, даже если она является всего лишь одной «точкой» в 3D-пространстве, имеет назначенное ей направление — vertex normal. Оно используется для вычисления того, как поверхность меша должна быть затененна. В этом примере я сделал шар и задал в Maya отображение vertex normals (display > polygons > vertex normals). У каждой вершины есть направление, которое по умолчанию указывает на расстояние от поверхности. Также можно редактировать любую vertex normal и изменять ее направление, но сейчас текущее нас устраивает. Теперь можно использовать его в шейдере и указать, в каком направлении сместить вершины.

0mulhbplxjfi_s6p0ndvgfn8f1a.jpeg

Vertex normal node выводит значения RGB, которые основаны на направлении нормали вершин меша. В шейдерах и с нормалями в целом значения RGB используются для выражения 3D-объекта координат XYZ в 2D-пространстве. Для того, чтобы наш черно-белый Panner в шейдере мог перемещаться вдоль поверхности 3D-объекта, мы можем использовать значения RGB vertex normal. Я добавил vertex color в вершины, чтобы низ меша стал более выпуклым. А также несколько нод для контроля величины смещения (это нормально, если ваши значения будут ниже 0 или выше 1).

utlo9ijazwjcvglbpmv4_r6j3-s.gif

Для демонстрации приведу сферу с vertex normal node, примененного непосредственно к альбедо (цветному) аутпуту главного нода.

cfbryjdr3a3aynhjsz9qnpr4zoy.jpeg

Вывод RGB (XYZ) vertex normal node основан на vertex normals меша, что дает нам такой результат.

Вот полная структура нодов. На ней видно, где и как все соединяется.

w1ssurjvayfyef_kfho-x_whtk8.jpeg

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

Еще несколько полезных ссылок по теме:

  • Доклад Джулиана Лава, который работал над Diablo в качестве VFX-художника. Выступление в основном касается навыков в эффектах, но многие из показанных вещей применимы и к другим типам realtime VFX.
  • Блог Little Chicken Game Company. Он посвящен созданию игрового арта без текстур и интересным техникам для создания красивого окружения.
  • У моего вдохновителя Саймона Трюмплера есть много других интересных вещей на сайте. Одна из моих любимых — страница с так называемыми Game Art Tricks.
  • YouTube-канал с роликами о шейдерах. Мои любимые — те, в которых воссоздают специфические эффекты из игр (в Unity).

© Habrahabr.ru