Есть игры с таким запоминающимся визуалом, что страшно считать потраченное на его разработку время, взять хотя бы последний God of War или RDR2. А бывают проекты, которые подкупают своей атмосферной, даже если они далеки от ААА. Как пример — RiME не раз хвалили за стиль, звук и историю. А недавно левел-дизайнер игры перешел в команду CD Projekt Red, чтобы работать над Cyberpunk 2077. Еще RiME вдохновила 3D-художника Math Roodhuizen на создание собственного арта.
И на фоне всего этого я наткнулся на ArtStation на гайд по созданию реалистичной воды. Если в прошлый раз речь шла про реку, то в этот раз про стилизованный водопад. В первой части разберем, как создавать «волновой эффект», да еще и прямо в игровом движке. Для удобства все шаги сопровождаются гифками и/или картинками.
Сначала скажу спасибо Саймону Трюмплеру — художнику из Tequila Games, который работал над RiME. Я был еще новичком в шейдерах и VFX, когда нашел его материал и подумал: «Ого, может я смогу сделать так же?». Рекомендую ознакомиться с докладом, чтобы узнать больше о способах создания стилизованных эффектов (таких как огонь, например).
А теперь поговорим про создание водопада. Особенно это будет полезно людям, которые еще не знакомы с системой Shader Graph. Наша цель: научиться новым способам работы и лучше разобраться в шейдерах.
Я буду показывать на примере Unity, но большинство методов применимы и в UE4 — если вы привыкли там работать, то просто повторяйте следом за мной. Для удобства каждый шаг визуализирован гифкой или картинкой.
Итак, вам понадобятся:
Сначала про «волновой эффект», который возникает, когда водопад падает на поверхность. Еще его можно использовать для других целей, например, для создания пульсаций вокруг предметов в воде.
Откройте Unity и убедитесь, что у вас установлен плагин для шейдеров. На момент написания статьи Unity имеет свой собственный Shader Graph, который еще разрабатывается. И пока он не готов, всё же рекомендую использовать Amplify Shader Editor для Unity 2018 или Shaderforge в Unity 2017.
Создайте новый материал и дайте ему имя (у меня это MAT_WaterWrinkles). Затем кликните правой кнопкой мыши на материал в Project Tab и перейдите к Create > Amplify Shader > Surface Shader. При этом новый шейдер автоматически применится.
Шейдер можно выбрать в любое время во вкладке Материалы > раскрывающийся список шейдеров (для Shaderforge будет что-то вроде PBL шейдера). Помните, что шейдер должен быть назван как в списке папок, так и в самом шейдере после его открытия.
Как только вы откроете новый шейдер, то увидите нечто такое:
Не буду писать подробное руководство по использованию Shader Graph, а если вы совсем новичок, то сначала почитайте вводные гайды.
В любом случае, чтобы получить желаемый эффект, сначала нам понадобится обычный Panner. Вот он с тестовой текстурой на плоскости:
Panner перемещает UV-координаты. Убедитесь, что Wrap Mode текстуры установлен на Repeat. В Shaderforge, возможно, придется подключить временный нод к инпуту Panner, у Amplify некоторые из этих базовых значений уже включены в самом ноде.
Итак, у нас есть движущаяся текстура. Но мы хотим добиться вот такого эффекта:
А для этого нужно, чтобы она прокручивалась вот так:
Как видите, структура нода та же самая — Panner просто перемещает UV-координаты в направлениях U и V (или X и Y). Чтобы получить нужное направление панорамирования, мы должны создать пользовательскую сетку с UV. Гифка для наглядности:
Применяя один и тот же материал к другой сетке, мы можем контролировать направление текстуры в 3D-пространстве, когда она двигается в UV-пространстве.
Для усиления эффекта, добавим еще несколько полигонов и исказим UV, чтобы текстура в центре двигалась быстрее, чем по краям. И разместим UV так, чтобы не было видно шва.
Из этого следует важный вывод: хорошие эффекты редко состоят из одной системы и часто представляют собой комбинацию нескольких.
Мы получили текстуру, двигающуюся в нужном направлении с правильной скоростью. UV-развертка размещена так, что перемещение справа налево в UV-пространстве с Panner превращается в движение внутрь и наружу в 3D-пространстве. Чтобы не было швов в текстуре на 3D-модели, UV-развертку надо приснепить (хоткей X для снэпа к сетке) к вершинам текстурного пространства по вертикали. В итоге мы добились желаемого эффекта.
Теперь прозрачность. Начнем с базовых вещей. Многие простые вычисления, которые вы делаете в шейдере, будут иметь значения от 0 (черный) до 1 (белый). То есть 0,5 — это оттенок серого, а 0,2 — оттенок темно-серого. При применении этих значений (в данном случае к каналу Opacity главного выходного нода), вы будете управлять уровнем прозрачности материала. Имейте в виду, что сначала нужно включить эту функцию. В Amplify можно изменить тип рендеринга (в режиме наложения) с непрозрачного на, например, прозрачный. В таком случае мы будем использовать тип рендеринга Transparent Cutout — пиксель, отображаемый этим материалом, либо полностью прозрачен, либо полностью непрозрачен. Это видно на гифке выше — там нет «полупрозрачных» пикселей, отображаемых этим шейдером.
Берем градиентную текстуру с шагом и помещаем ее в шейдер. Здесь она подключается к выходу альбедо (цветному) основного нода.
А тут она подключается к выходу Opacity Mask (этот параметр включается при типе рендеринга Transparent Cutout).
Видно, что градиентная текстура использует черные и белые значения пикселей, чтобы определить прозрачность или непрозрачность. Всё от 1 (белый) до 0,5 (серый) становится непрозрачным, а всё от 0,5 (серый) до 1 (черный) — полностью прозрачным (или вообще не отображается). Opacity Mask создает жесткий отрезок и округляет значения до 0 или 1 в зависимости от того, какое число ближе.
Еще одна текстура в оттенках серого:
Подключена к Opacity Mask:
Идею вы поняли. И догадываетесь, как ее можно использовать:
Вы видите в основном синее изображение, прокручивающееся в Shader Graph. И то, что я использую только R (красный) выход этого нода. Ради оптимизации я упаковал две grayscale-текстуры (текстуры в оттенках серого) в один файл, но это необязательно. Подобные изображения можно упаковать с помощью Photoshop или Substance Designer.
Эффект пульсации — хорошее начало, но можно лучше. Почему бы не наложить друг на друга две grayscale-текстуры, чтобы сделать эффект ряби более рандомным?
Я использую одну и ту же текстуру, но добавляю разные каналы. Прокручиваю их с разной скоростью и немного в разных направлениях. Текстура имеет хороший градиент значений серого, что дает ощущение случайного волнового эффекта.
На следующей гифке есть еще одна вещь, которую мы пока не обсудили — поэтому ваш вариант может выглядеть несколько иначе:
Видно, как, например, пиксель со значением 0,2 (который не рендерится) проходит поверх пикселя со значением 0,4 (который тоже не рендерится) и внезапно становится пикселем со значением 0,6 — поскольку мы используем дополнительный нод (0,2 добавляется к 0,4 и получается 0,6). Добавление этих двух волновых grayscale текстур поверх друг друга с разной скоростью и дает близкий к желаемому эффект.
Вот, чтобы продемонстрировать результат, я добавил grayscale-текстуры друг на друга в Photoshop.
Тем не менее, шов в конце сетки все еще остается отчетливым. А нам нужно получить это:
Вместо этого:
На первой из двух гифок пульсации медленно стихают и становятся меньше, когда они приближаются к краю сетки. Самый простой способ это сделать — использовать vertex color (цвета вершин).
Каждая вершина модели содержит свои данные (например, координаты X, Y и Z), а также цвет, который имеет значение от 0 до 1. Цвет вершинам можно задать и в 3D-редакторе.
Большинство внешних вершин — черные (то есть имеют значение 0). Они становятся белыми (ближе к 1) при приближении к центру. Обратите внимание, вам понадобятся дополнительные Subdivisions, чтобы получить вершины, которые можно раскрасить.
Например, в Maya для раскрашивания можно перейти в меню Mesh Display > Paint Vertex Color и нажать на поле More Options.
Теперь мы можем соединить Vertex Color в нашем шейдере с помощью Multiply.
Я наложил градиент поверх используемых текстур и установил режим слоя на Multiply.
Градиент усиливает значение серого до 0 (черный), поэтому отрендерить пиксели будет сложнее (они же не отображаются ниже значения 0,5). Это приводит к тому, что линии пульсаций сжимаются ближе к краю (цвета вершин в основном выступают в качестве градиента).
Я добавил еще несколько нодов, чтобы контролировать толщину линий пульсаций. Вот полный граф:
Просто поэкспериментируйте со скоростью/направлением прокручивания, настройте текстуру и попробуйте изменить тайлинг, чтобы выжать из эффекта максимум.
Это поможет нам при создании более сложного шейдера — водопада. О нем поговорим в следующей части.
И на фоне всего этого я наткнулся на ArtStation на гайд по созданию реалистичной воды. Если в прошлый раз речь шла про реку, то в этот раз про стилизованный водопад. В первой части разберем, как создавать «волновой эффект», да еще и прямо в игровом движке. Для удобства все шаги сопровождаются гифками и/или картинками.
Сначала скажу спасибо Саймону Трюмплеру — художнику из Tequila Games, который работал над RiME. Я был еще новичком в шейдерах и VFX, когда нашел его материал и подумал: «Ого, может я смогу сделать так же?». Рекомендую ознакомиться с докладом, чтобы узнать больше о способах создания стилизованных эффектов (таких как огонь, например).
А теперь поговорим про создание водопада. Особенно это будет полезно людям, которые еще не знакомы с системой Shader Graph. Наша цель: научиться новым способам работы и лучше разобраться в шейдерах.
Я буду показывать на примере Unity, но большинство методов применимы и в UE4 — если вы привыкли там работать, то просто повторяйте следом за мной. Для удобства каждый шаг визуализирован гифкой или картинкой.
Итак, вам понадобятся:
- Unity 2018.
- Amplify Shader Editor (плагин Unity). Но если не хотите тратить на него деньги — установите Unity 2017 и скачайте бесплатный Shaderforge из магазина ассетов (новая версия движка не поддерживает этот плагин, но он все еще работает в Unity 2017). Оба инструмента практически одинаковы, разве что некоторые моменты называются по-разному.
- Autodesk Maya.
- Что-нибудь для создания или редактирования текстур. Я использую Photoshop, а иногда даже Substance Designer (но для этого водопада он вам точно не понадобится).
Сначала про «волновой эффект», который возникает, когда водопад падает на поверхность. Еще его можно использовать для других целей, например, для создания пульсаций вокруг предметов в воде.
Откройте Unity и убедитесь, что у вас установлен плагин для шейдеров. На момент написания статьи Unity имеет свой собственный Shader Graph, который еще разрабатывается. И пока он не готов, всё же рекомендую использовать Amplify Shader Editor для Unity 2018 или Shaderforge в Unity 2017.
Создайте новый материал и дайте ему имя (у меня это MAT_WaterWrinkles). Затем кликните правой кнопкой мыши на материал в Project Tab и перейдите к Create > Amplify Shader > Surface Shader. При этом новый шейдер автоматически применится.
Шейдер можно выбрать в любое время во вкладке Материалы > раскрывающийся список шейдеров (для Shaderforge будет что-то вроде PBL шейдера). Помните, что шейдер должен быть назван как в списке папок, так и в самом шейдере после его открытия.
Как только вы откроете новый шейдер, то увидите нечто такое:
Не буду писать подробное руководство по использованию Shader Graph, а если вы совсем новичок, то сначала почитайте вводные гайды.
В любом случае, чтобы получить желаемый эффект, сначала нам понадобится обычный Panner. Вот он с тестовой текстурой на плоскости:
Panner перемещает UV-координаты. Убедитесь, что Wrap Mode текстуры установлен на Repeat. В Shaderforge, возможно, придется подключить временный нод к инпуту Panner, у Amplify некоторые из этих базовых значений уже включены в самом ноде.
Итак, у нас есть движущаяся текстура. Но мы хотим добиться вот такого эффекта:
А для этого нужно, чтобы она прокручивалась вот так:
Как видите, структура нода та же самая — Panner просто перемещает UV-координаты в направлениях U и V (или X и Y). Чтобы получить нужное направление панорамирования, мы должны создать пользовательскую сетку с UV. Гифка для наглядности:
Применяя один и тот же материал к другой сетке, мы можем контролировать направление текстуры в 3D-пространстве, когда она двигается в UV-пространстве.
Для усиления эффекта, добавим еще несколько полигонов и исказим UV, чтобы текстура в центре двигалась быстрее, чем по краям. И разместим UV так, чтобы не было видно шва.
Из этого следует важный вывод: хорошие эффекты редко состоят из одной системы и часто представляют собой комбинацию нескольких.
Мы получили текстуру, двигающуюся в нужном направлении с правильной скоростью. UV-развертка размещена так, что перемещение справа налево в UV-пространстве с Panner превращается в движение внутрь и наружу в 3D-пространстве. Чтобы не было швов в текстуре на 3D-модели, UV-развертку надо приснепить (хоткей X для снэпа к сетке) к вершинам текстурного пространства по вертикали. В итоге мы добились желаемого эффекта.
Теперь прозрачность. Начнем с базовых вещей. Многие простые вычисления, которые вы делаете в шейдере, будут иметь значения от 0 (черный) до 1 (белый). То есть 0,5 — это оттенок серого, а 0,2 — оттенок темно-серого. При применении этих значений (в данном случае к каналу Opacity главного выходного нода), вы будете управлять уровнем прозрачности материала. Имейте в виду, что сначала нужно включить эту функцию. В Amplify можно изменить тип рендеринга (в режиме наложения) с непрозрачного на, например, прозрачный. В таком случае мы будем использовать тип рендеринга Transparent Cutout — пиксель, отображаемый этим материалом, либо полностью прозрачен, либо полностью непрозрачен. Это видно на гифке выше — там нет «полупрозрачных» пикселей, отображаемых этим шейдером.
Берем градиентную текстуру с шагом и помещаем ее в шейдер. Здесь она подключается к выходу альбедо (цветному) основного нода.
А тут она подключается к выходу Opacity Mask (этот параметр включается при типе рендеринга Transparent Cutout).
Видно, что градиентная текстура использует черные и белые значения пикселей, чтобы определить прозрачность или непрозрачность. Всё от 1 (белый) до 0,5 (серый) становится непрозрачным, а всё от 0,5 (серый) до 1 (черный) — полностью прозрачным (или вообще не отображается). Opacity Mask создает жесткий отрезок и округляет значения до 0 или 1 в зависимости от того, какое число ближе.
Еще одна текстура в оттенках серого:
Подключена к Opacity Mask:
Идею вы поняли. И догадываетесь, как ее можно использовать:
Вы видите в основном синее изображение, прокручивающееся в Shader Graph. И то, что я использую только R (красный) выход этого нода. Ради оптимизации я упаковал две grayscale-текстуры (текстуры в оттенках серого) в один файл, но это необязательно. Подобные изображения можно упаковать с помощью Photoshop или Substance Designer.
Эффект пульсации — хорошее начало, но можно лучше. Почему бы не наложить друг на друга две grayscale-текстуры, чтобы сделать эффект ряби более рандомным?
Я использую одну и ту же текстуру, но добавляю разные каналы. Прокручиваю их с разной скоростью и немного в разных направлениях. Текстура имеет хороший градиент значений серого, что дает ощущение случайного волнового эффекта.
На следующей гифке есть еще одна вещь, которую мы пока не обсудили — поэтому ваш вариант может выглядеть несколько иначе:
Видно, как, например, пиксель со значением 0,2 (который не рендерится) проходит поверх пикселя со значением 0,4 (который тоже не рендерится) и внезапно становится пикселем со значением 0,6 — поскольку мы используем дополнительный нод (0,2 добавляется к 0,4 и получается 0,6). Добавление этих двух волновых grayscale текстур поверх друг друга с разной скоростью и дает близкий к желаемому эффект.
Вот, чтобы продемонстрировать результат, я добавил grayscale-текстуры друг на друга в Photoshop.
Тем не менее, шов в конце сетки все еще остается отчетливым. А нам нужно получить это:
Вместо этого:
На первой из двух гифок пульсации медленно стихают и становятся меньше, когда они приближаются к краю сетки. Самый простой способ это сделать — использовать vertex color (цвета вершин).
Каждая вершина модели содержит свои данные (например, координаты X, Y и Z), а также цвет, который имеет значение от 0 до 1. Цвет вершинам можно задать и в 3D-редакторе.
Большинство внешних вершин — черные (то есть имеют значение 0). Они становятся белыми (ближе к 1) при приближении к центру. Обратите внимание, вам понадобятся дополнительные Subdivisions, чтобы получить вершины, которые можно раскрасить.
Например, в Maya для раскрашивания можно перейти в меню Mesh Display > Paint Vertex Color и нажать на поле More Options.
Теперь мы можем соединить Vertex Color в нашем шейдере с помощью Multiply.
Я наложил градиент поверх используемых текстур и установил режим слоя на Multiply.
Градиент усиливает значение серого до 0 (черный), поэтому отрендерить пиксели будет сложнее (они же не отображаются ниже значения 0,5). Это приводит к тому, что линии пульсаций сжимаются ближе к краю (цвета вершин в основном выступают в качестве градиента).
Я добавил еще несколько нодов, чтобы контролировать толщину линий пульсаций. Вот полный граф:
Просто поэкспериментируйте со скоростью/направлением прокручивания, настройте текстуру и попробуйте изменить тайлинг, чтобы выжать из эффекта максимум.
Это поможет нам при создании более сложного шейдера — водопада. О нем поговорим в следующей части.