Туториал по созданию эффекта сел-шейдинга в Unity
Поэтапный рассказ с примерами кода.
Эффект сел-шейдинга используется для стилизации графики под мультфильм — благодаря этому 3D-объекты выглядят как 2D-спрайты. Эта техника рендеринга стала популярной после нескольких крайне успешных игр, к примеру, Jet Set Radio и The Legend of Zelda: The Wind Waker.
Независимый разработчик Эрик Ройстен Росс в 2018 году в собственном блоге опубликовал туториал, в котором подробно рассказал, как реализовать эффект сел-шейдинга в Unity. Мы выбрали из текста главное.
Основной референс в этом туториале — The Legend of Zelda: Breath of the Wild.
Сначала скачайте стартовый проект и откройте его в Unity. Этот файл содержит простой шейдер — по умолчанию текстура окрашена в синий цвет.
Направленный источник света
Если дело касается шейдеров, которые взаимодействуют с освещением, то обычно используются Surface Shaders — они позволяют автоматизировать взаимодействие объекта со светом и глобальным освещением. Но в нашем случае шейдер будет взаимодействовать только с направленным источником светом, поэтому в Surface Shaders нет необходимости.
Первым делом нужно сделать так, чтобы шейдер получал данные об освещении. Добавьте следующий код в верхней части Pass — сразу после фигурной скобки.
Первая строка запрашивает данные освещения для передачи в шейдер, а вторая фильтрует всё кроме направленного источника света.
Чтобы рассчитать освещение, нужно использовать общую модель затенения Блинна-Фонга, а также настроить дополнительные фильтры, чтобы придать шейдеру мультяшный вид. Теперь нужно рассчитать количества света, которое падает на поверхность от направленного источника — оно пропорционально нормали поверхности относительно направления света.
Шейдер должен учитывать данные нормалей, поэтому потребуется следующий код.
Нормали в appdata заполняются автоматически, в то время как значения в v2f должны быть введены вручную в vertex shader. Также нужно преобразовать нормаль из object space в world space, поскольку направление света связано именно с world space, Добавьте следующую строку в vertex shader.
Теперь нормаль можно сопоставить с направлением света при помощи скалярного произведения.
Скалярное произведение сравнивает два вектора и выдаёт простое число, основываясь на том, под каким углом они находятся по отношению друг к другу. Если векторы параллельны, то число равно 1, если перпендикулярны, то 0. Когда угол между векторами больше 90, скалярное произведение будет отрицательным. Добавьте в шейдер следующий код.
Теперь сфера выглядит более реалистично. Чтобы придать ей мультяшности, нужно разделить освещение на две зоны: светлую и тёмную.
Рассеянный свет
Следующий шаг — добавление рассеянного света, который отражается от поверхности объектов и рассеивается в атмосфере. В нашем случае этот свет будет воздействовать на все поверхности одинаково.
Чтобы можно было менять интенсивность или цвет направленного света, нужно добавить ещё одну часть кода.
Пора смягчить границу между светом и тенью. Для этого используется функция smoothstep: у неё есть три значения — нижняя граница, верхняя граница и значение между ними. Важно понимать, что функция smoothstep не линейная: в значениях от 0 до 0,5 она «усиливается», а в значениях от 0,5 до 1 — «ослабляется».
Отражения
Следующий шаг — добавление отражений: они зависят от угла, под которым на него смотрят. Поэтому нужно рассчитать world view direction в vertex shader.
Теперь мы переходим к реализации модели отражений Блинна-Фонга. Этот расчёт учитывает два свойства поверхности: цвет отражения и то, насколько хорошо поверхность отражает свет.
Сила отражения в модели Блинна-Фонга определяется как скалярное произведение между нормалью поверхности и half-вектором — вектором между углом обзора и источником света. Чтобы получить half-вектор, нужно суммировать эти два вектора, а затем нормализовать результат.
Функция pow контролирует размер отражения. Также нужно умножить NdotH на lightIntensity, чтобы гарантировать, что отражения появляются только в том случае, если поверхность освещена.
Затем нужно ещё раз использовать smoothstep для усиления отражения, а также умножить конечный результат на _SpecularColor.
Контровое освещение
Контровое освещение обычно используется для имитации отражённого света или источника света, находящегося за предметом. Это особенно важно для сел-шейдинга, так как помогает объектам не сливаться с фоном.
Само контровое освещение можно определить как поверхность, которая обращена в сторону от камеры. Чтобы вычислить это освещение, нужно взять скалярное произведение нормали и направления камеры, а затем инвертировать его.
Чтобы усилить эффект мультяшности, нужно установить пороговое значение с помощью smoothstep.
Так как этот вариант контрового света наблюдается вокруг всего объекта, возникает ощущение, что это обводка. Теперь нужно сделать так, чтобы контровой свет был только на освещённой части объекта.
С помощью функции pow можно масштабировать контровой свет.
Тени
Финальный шаг — добавление возможности отбрасывать тени. Поставьте следующий фрагмент перед Pass.
Намного сложнее реализовать возможность, при которой тени будут падать на сам объект. Для этого нужно сделать так, чтобы шейдер «знал», когда объект попадает в тень — нужно перенести координаты текстуры из vertex shader в fragment shader.
Для реализации этого, нужно добавить Autolight.cginc — файл, содержащий несколько макросов, которые используются для определения теней. SHADOW_COORDS (2) генерирует значение для четырёх измерений с меняющейся точностью и присваивает его семантике TEXCOORD по заданному индексу (в нашем случае 2).
TRANSFER_SHADOW преобразует пространство вершины в пространство теневой карты, а затем сохраняет его в SHADOW_COORD.
Но перед этим нужно убедиться, что шейдер адекватно реагирует на разные условия освещения. Unity поможет справиться с этим — есть несколько вариантов решения этой задачи.
Это показывает Unity, что нужно скомпилировать все варианты, необходимые для рендеринга. Теперь можно выбрать значение на карте теней и применить его к расчёту освещения.
В результате получается эффект сел-шейдинга, имитирующий 2D-стиль рисования.
Комментарий недоступен
жесть ты крут
Уух, тоже замотивировался. Вот все, начинаю работу и сделаю тоже крутой проект.
...
А нет, все я потерял интерес, проект заброшен.
вот ты молодец! иди и делай! мы в тебя верим!
Комментарий недоступен
У меня этот тутор настолько долго в закладках лежал, что его уже успели перевести...
Unity мертвый движок. Оставьте вы его уже.