Как сделать шейдеры природы с помощью Shader Graph в 2022 LTS

В этом блоге мы рассмотрим, как создать два разных шейдера природы с помощью Universal Render Pipeline (URP) в 2022 LTS. Мы также подробно рассмотрим стилизованный шейдер воды и полуреалистичный шейдер песка. Активы выпущены вместе с новыми образцами сцен URP 3D.
На первый взгляд визуальные эффекты кажутся сложными, но мы погрузимся в процесс продумывания дизайна, лежащий в основе этих функций, и пошагово разберем приемы, с помощью которых они воплощаются в жизнь. Давайте изучим тонкости разработки шейдеров и создадим потрясающие природные шейдеры.
Цель, как показано в видеоролике выше, - создать стилизованный ручей, протекающий посреди садовой сцены в японском стиле. Судя по остальному окружению, атмосфера здесь спокойная и дзеноподобная, а художественный стиль скорее анимированный, чем фотореалистичный.
Судя по рельефу, вписанному в сцену, вода представляет собой сочетание двух частей: водопада и ручья, протекающего под мостом. Водные сцены Studio Ghibli очень вдохновляют, в них часто присутствуют три разных элемента:
1. Проточные линии, которые помогают установить поток воды
2. Выделение краев, чтобы показать взаимодействие воды с окружающей местностью
3. Пенные эффекты, помогающие реализовать каскады или водопады
В финальном шейдере воды будут использованы все эти элементы. Теперь давайте разберемся, как добиться такого образа.
Водопад состоит из двух отдельных сеток - основной сетки водопада и дискообразной плоскости, создающей рябь. Используя отдельный неосвещенный шейдер для ряби, вы можете выложить плиткой узел шума и использовать его в качестве альфа-значения шейдера. Это маскирует оставшиеся области, обеспечивая появление ряби в нужных местах.

Самое важное - обеспечить, чтобы каждая область сетки могла выполнять различные действия. Этого можно добиться, предварительно окрасив сетки цветами вершин в красном (R), зеленом (G) и синем (B) каналах. Цвета вершин затем используются в качестве масок для разделения операций в определенных областях.
В Shader Graph используйте узел Vertex Color Node для доступа к предварительно нарисованным данным о цвете вершин. Как видно на правой стороне изображения ниже, вы можете использовать красный канал в цвете вершины в качестве значения T для интерполяции (lerp) между вертикальной и горизонтальной частью водопада, добиваясь плавного перехода. Чтобы создать эффект каскада воды, объедините два узла Вороного, каждый из которых имеет разную укладку и смещение. В результате создается динамичное изображение падающей воды.

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

Теперь, когда вы создали успокаивающий водопад, перейдем к ручью. Есть несколько ключевых особенностей, которыми обычно обладает вода, стилизованная под анимацию. В отличие от турбулентной береговой пены, встречающейся в реальном мире, поток в стиле Гибли обычно имеет очень тонкую пену у берега. Кроме того, наличие водяных хвостов может придать воде динамичность и живость. Поскольку действие происходит ночью, необходим убедительный эффект отражения. Давайте подробнее рассмотрим, как можно добиться этих эффектов в Shader Graph.
Существует множество способов запечатлеть отражение на поверхности воды. Наиболее эффективным вариантом является узел пользовательской функции, который вызывает функцию GlossyEnvironmentReflection, встроенную в URP. Эта функция возвращает цвет отражения, который сэмплируется из проекционного датчика отражения в сцене. Вам просто нужно передать в функцию положение в мировом пространстве, направление обзора, нормаль и положение на экране, которые требуются.

Если вам нужно более высокое визуальное качество и более приземленные отражения, то планарные отражения URP могут стать отличным вариантом. Плоскостное отражение создает зеркальное отражение плоской поверхности, что идеально подходит для водной сетки, учитывая ее плоскую структуру.
Чтобы добиться планарного отражения, необходимо создать отдельную камеру и текстуру рендеринга для хранения данных об отражении. Основная концепция заключается в том, чтобы разместить камеру отражения под плоскостью отражения (в данном случае - потока в сцене) и обновлять ее в зависимости от положения и ориентации камеры игрока. Текстура рендера также будет обновляться в режиме реального времени.
Одно из преимуществ заключается в том, что вы можете задать ближнюю плоскость камеры отражения в качестве самой плоскости отражения. Это избавляет от необходимости вырезать объекты, расположенные ниже плоскости отражения, что упрощает процесс реализации. В Shader Graph вы создаете свойство текстуры, а в скрипте назначаете ему созданную ранее текстуру рендеринга.
Чтобы успешно связать текстуру рендеринга, убедитесь, что при установке свойства шейдера заданное свойство совпадает с идентификатором ссылки свойства текстуры, созданного в Shader Graph. Убедитесь, что скрипт вызывает точный ID свойства при обновлении текстуры рендеринга. Затем используйте положение экрана в качестве UV для выборки текстуры. Теперь вы успешно добились планарного отражения в нашем шейдере.

Реализация планарного отражения включает в себя несколько технических моментов и деталей. Для более глубокого понимания и примера его реализации посмотрите этот образец URP. Стоит отметить, что планарное отражение требует больших вычислительных затрат по сравнению с использованием зондов отражения, поскольку при этом объекты рендерятся дважды.
Чтобы добиться эффекта кромочной пены, необходимо рассчитать разницу в глубине. Параметр Linear01 в Scene Depth Node возвращает значение линейной глубины, масштабированное от 0 до 1 для непрозрачных объектов. Умножив это значение на расстояние до дальней плоскости камеры, вы сможете определить расстояние между камерой и непрозрачным объектом - в данном случае скалой. Компонент z параметра Raw в узле Screen Position обеспечивает глубину пространства для глаз. Затем вы можете легко рассчитать разницу в глубине между прозрачной поверхностью воды и непрозрачной скалой и передать значение глубины в выход Emission для создания эффекта, напоминающего пену.

Чтобы получить значения глубины из сцены, убедитесь, что в настройках проекта включена функция Depth Texture. Опцию Depth Texture можно найти в разделе General актива конвейера рендеринга. Текущий актив конвейера рендеринга доступен через Edit > Project Setting > Graphics > Render Pipeline Asset.

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


Теперь давайте сменим передачу и посмотрим на менее стилизованный, более реалистичный шейдер. Шейдер песка находится в реалистичной сцене пустыни, что требует, чтобы местность визуально очень напоминала реальный песок.
Песочный рендеринг - интересная задача. Обычный PBR-шейдер не передаст вид пустынного песка под сильным солнцем. В шейдере песка есть две основные особенности: блеск песка, который является тонким, но примечательным аспектом, и сдуваемая пыль - динамическая особенность, которая добавляет живости.
Поскольку песок состоит из бесчисленных мелких песчинок, он искрится под воздействием солнечного света. Как добиться такого эффекта блеска? Аналогично тому, как вы делаете зеркальное отражение, сначала рассчитайте вектор отражения, используя нормаль поверхности. Вдохновение от Journeyвместо того, чтобы вычислять точечное произведение между вектором нормали и вектором половины пути, вы вычисляете точечное произведение между нормалью и направлением взгляда. Благодаря этой настройке рисунок блесток меняется в зависимости от углов обзора, что повышает визуальную привлекательность шейдера.
У вас две карты шума в шейдере. Один используется для сэмплирования блесток, а другой - для маскировки основной шумовой текстуры, чтобы получить более динамичный и захватывающий результат. Используйте рассчитанный ранее вектор отражения для искажения ультрафиолета, используемого для выборки маски шума.

Эффект пылящего песка - это сочетание двух элементов: движущихся песчаных дорожек и песчаных волн. Концепция довольно проста. Вы накладываете различные карты нормалей, чтобы добиться желаемого результата. Ключевым моментом для песчаных дорожек является то, что вам нужна маска, чтобы скрыть карту нормалей и придать эффекту более динамичный вид. Вместо того чтобы использовать стандартный UV, возьмите две карты шума с абсолютным положением мира.
Стоит отметить, что параметр World в Position Node меняется в зависимости от настроек различных конвейеров рендеринга, поэтому выберите параметр Absolute World, чтобы избежать изменений в поведении при переключении конвейеров. Затем наложите плитки на две карты по диагонали, что создаст эффект ряби. Затем прокрутите еще одну карту шума вдоль направления песчаной волны, чтобы добавить ощущение смещения песка.

Важным аспектом является то, как добиться нормального смешивания в шейдере. На песчаной местности, где карта альбедо может быть не такой сложной по сравнению с другими местностями, нормали играют значительную роль в визуальном восприятии. Смешивание нескольких карт нормалей в шейдере. В отличие от смешивания карт альбедо, карты нормалей хранят направления, и различные методы смешивания могут дать совершенно разные результаты.
В качестве примера рассмотрим узел Normal Blend в Shader Graph. При смешивании карт нормалей A и B опция по умолчанию в узле Normal Blend складывает каналы x и y двух карт вместе, а каналы z перемножает, чтобы получить третий элемент смешанной нормали. Переориентированный вариант, как следует из названия, предполагает более сложный процесс. Он поворачивает нормали карты B так, чтобы они совпадали с направлением карты A. Этот подход сохраняет наибольшее количество данных с обеих карт, но он также является наиболее затратным с точки зрения вычислений.

В нашем шейдере мы выбрали простой метод смешивания для нормалей. Основная причина смешивания нормалей заключается в том, чтобы создать яркое ощущение того, что песок дует или движется по поверхности пустыни. Точность не является главным приоритетом. Кроме того, шейдер применяется к относительно большой сетке местности, поэтому минимизация вычислительных затрат имеет большое значение.
Учитывая все эти факторы, мы предлагаем вам простой подход: Сложите красный и зеленый каналы карт нормалей вместе, а для синего канала передайте значение 1. Затем немного увеличьте масштаб обычной силы, и результат выглядит великолепно.

Помимо рассмотренных функций, в шейдере реализованы и другие элементы управления. Один из них - общий контроль затухания, который определяет, где на местности появляются эффекты в зависимости от расстояния до камеры. Это позволяет добиться постепенного перехода и затухания эффектов по мере удаления камеры.
Вы также можете отрегулировать значение сглаживания на расстоянии, чтобы песчаные дюны лучше сочетались с фоновым рельефом. По мере приближения зрителя детальная карта нормалей зернистости заменяет нормали рельефа. Эта замена обеспечивает более реалистичное восприятие пустыни, добавляя более мелкие детали и текстуры на поверхность песка.
Теперь, когда вы ознакомились со всеми возможностями каждого из этих двух шейдеров, не стесняйтесь создавать свои собственные версии. Если вы увлечены созданием шейдеров с помощью Shader Graph, присоединяйтесь к нашему форуму или найдите нас в Discord. Обязательно следите за дальнейшими техническими обзорами от разработчиков Unity в рамках продолжающейся серии Tech from the Trenches.
