Шейдинг что это такое
Что такое PBR? Часть II
В предыдущей статье, для себя и для неравнодушных, прояснил, что стоит за термином PBR.
Physically Based Shading (PBS)
Шейдер (англ. Shader – затеняющий), шейдинг (англ. Shading – затенение). В CG это определение перекочевало из области изящных искусств. Так называют технику штриховки карандашом, которая выявляет объем объекта.
Если мы нарисуем замкнутую линию, ну, например, пусть это будет круг (Аналогом в 3ds max может быть сплайн). Данная геометрическая фигура не дает нам никакого представления об объеме объекта и я уже не говорю о свойствах его поверхности и т.д.
Если нарисуем несколько замкнутых линий эллипсовидной формы, то они образуют каркас сферы (аналогом такого решения будет отображение сферического объекта в режиме «wire»). Текущее представление, уже может дать представление о форме объекта, но для людей с хорошим пространственным мышлением.
Если же начать наносить штрих, поверх конструктивного рисунка, выявляя границы между светом и тенью, добавляя полутени, а также обозначив границы у падающих теней, то получится объемно-пространственное изображение. Эти действия можно сопоставить с процессом рендеринга, а итог этих действий – с результатами рендеринга.
BxDF функции
Популярная и широко используемая в продакшен рендеринге рендер система V-ray долгое время применяла в стандартном шейдере VrayMtl следующие функции:
Phong
Blinn
Данные модели BRDF не имеют ничего общего с PBR, поскольку разработаны на основе эмпирических данных.
Удивительно, но да, большинство BxDF функций реализованы «на глазок». Другое дело функция GGX. Данная функция была выведена на основе физических измерений и в различных имплементациях используется во всех современных рендер системах, как в продакшене, так и в реалтайме.
GGX функция
Длинный «хвост» или эффект «дымки» вокруг основного блика обусловлен тем, что большинство объектов, которые встречаются в обычных бытовых условиях, не имеют идеальной полированной поверхности у них всегда имеются микрошероховатости, которые оказывают влияние на глянцевитость поверхности.
Этот недостаток у GGX функции был решен в R&D (англ. Research and Development — исследования и разработки) отделе студии Дисней, который модифицировал GGX функцию, «скрестив» её с зеркальной моделью Trowbridge-Reitz описанную в работе 1975 года «Average Irregularity Representation of a Rough Surface for Ray Reflection». Таким образом в GGX функции появилась переменная для управления «хвостом» блика. Самой функции дали название GTR (Generalized Trowbridge-Reitz) Продолжение следует…
Полезные советы по оптимизации игровой модели. Часть 1
Shading
Шейдинг — это подложка под запекание нормалки. Чем лучше он выглядит, тем красивее будет финальная модель и normal map. Слева на скриншоте пример хорошего шейдинга, справа — плохого.
То есть слева low poly модель, максимально похожая на high poly: выглядит красиво, нет никаких артефактов и искажений. Справа видны ошибки в шейдинге, искажения.
Основные инструменты воздействия на шейдинг — это добавление большего количества геометрии. Если этот вариант не подходит, — проставление хардов. Изначально шейдинг модели на гифке ниже недостаточно корректно отображает ее форму. С добавлением edge loops модель шейдится лучше. Ее форма становится понятней.
Это самый лучший, но и самый затратный способ, потому что используется большое количество полигонов. Если у вас ограничения по полигонам, можно использовать харды. Однако в этом случае придется делать разрез на UV.
Поэтому хороший шейдинг — это баланс между софтами и хардами.
Совет всем начинающим: настраивайте шейдинг во время low poly стадии. Таким образом вы прямо по ходу ретопологии сможете понять, где добавить дополнительную геометрию, а где поставить hard edge.
Многие сначала делают ретопологию, разворачивают, запаковывают и только потом настраивают шейдинг по хардам. Это не самый корректный способ. Результат будет более оптимальным, если изначально опираться на шейдинг.
Texel Density
Это нужно для того, чтобы абсолютно все объекты на уровне имели одинаковое разрешение текстур. Предположим, на уровне есть две бочки — одна маленькая, другая большая. Если мы применим к ним одинаковую текстуру в 2К, плотность их текстур будет отличаться. Маленькая бочка будет крайне плотная и детализированная, большая будет замыленная. Чтобы избежать этих проблем появился термин Texel Density.
Очень часто бывает, что клиент задает необходимое значение Texel Density, но не задает разрешение текстур. Раньше это создавало трудности. Приходилось колхозить. Сейчас в UV Editor Maya 2018 есть специальный инструмент. Выбираете необходимый UV Shell, нажимаете Get и вам выдает числовое значение, в зависимости от разрешения текстур. Точно так же можно выбрать любой Shell, задать цифру, нажать Set и Texel Density будет такой, как вы задали.
Распределение Texel Density.
Предположим, что желтый цвет — это Texel Density, которого мы должны придерживаться. Чем краснее цвет, тем его значение больше, текстура плотнее, детализация выше. И наоборот, — чем синее, тем значение меньше.
Прием с увеличением значения используется, в основном, в оружии от первого лица. Потому что, заднюю часть пистолета мы видим больше всего, и было бы неплохо сделать ее более детализировано.
С другой стороны, уменьшить детализацию там, куда никто не смотрит — это прием из разряда «must have». Абсолютно независимо от того, какой объект вы делаете.
Для одного из тестовых я делала квадратный генератор. Его днище, которое в принципе никогда никто не увидит, я разювишил точно так же, как и все остальное. То есть оно занимало ¼ моего UV пространства. Это было абсолютно не оптимальное использование текстуры. Для экономии пространства, в невидимых местах, заполненных геометрией, необходимо использовать крайне маленькие шеллы текстур.
Reuse
Повторное использование текстур — еще один не менее важный инструмент. Я использую его даже в работах для портфолио, потому что он тоже экономит много пространства.
Данный прием экономит не только пространство на UV, но и время на текстуринге, потому что текстурить вам нужно только одну сторону. Но есть и небольшой минус. Надписи, логотипы, цифры, текст, будут отображены зеркально. В таких случаях тоже есть уловка: можно использовать символы, которые читаются с двух сторон. Это жертвы, на которые стоит идти, чтобы, убрав лишнюю работу, повысить качество модели.
Ровные UV Shells
Для примера, я запек два кубика с одинаковым Texel Density и текстурой. Но в кубе слева все UV Shells выровнены по горизонтали и вертикали, а справа повернуты под углом.
Ровные шеллы создают хорошую, красивую фаску. Если же шеллы повернуты, мы видим эффект лесенки. Это крайне некрасиво. Особенно на игровых разрешениях текстур. Потому что в играх используется маленькое разрешение, на котором это очень заметно. Поэтому любой прямоугольный шелл нужно выравнивать. Даже если это не идеальная прямоугольная форма на самом объекте, я его специально выравниваю, чтобы на UV он был ровный.
Mip Mapping и Padding
Если игрок отдаляется от какого-то игрового объекта, то модель этого объекта меняется на менее полигональную. Точно так же с текстурами — чем дальше мы отходим, тем меньшая текстура к нему применяется. 4К подменяется на 2К, когда мы отходим еще дальше — на 1К. Этот прием экономит ресурсы вашего компьютера.
Padding — это расстояние между UV Shells. Чем меньше это расстояние, тем сложнее отображать текстуры при Mip Mapping. Потому что при уменьшении текстур, уменьшается и расстояние между шеллами. Padding нужно всегда делать побольше, или хотя бы следить за тем, чтобы он везде был равномерным. Для персональных работ, в принципе, все равно, но если вы делаете проекты для клиентов, на это следует обращать внимание.
Триангуляция
Это очень важный момент. Изначально полигоны прямоугольные — квады. Соединив два вертекса, получается два треугольника.
Любой движок, работающий с геометрией, триангулирует модель, чтобы корректно ее отобразить.
Если оставить квады на ровной поверхности, никаких проблем не будет. Но бывают не идеально ровные полигоны. И вот тут уже, в зависимости от того, как мы соединяем вертексы, меняется форма. В одном случае он вогнутый, в другом выпуклый.
Если запекать в квадах, то бейкер триангулирует модель каким-то своим образом, и на базе этой триангуляции выдает нормалку. Если затем эту модель закинуть в движок, то не факт, что он триангулирует ее также. Из-за несоответствия нормалки с триангуляцией, появятся ошибки и артефакты.
Чтобы быть уверенным, что ваша нормалка будет везде красиво смотреться, триангулируйте меш до запекания.
Градиенты на нормалях
Если вы все правильно сделали, нормалка будет выглядеть как на картинке слева. Она стремиться к однородному цвету, а градиентная информация находится по фаскам. Справа пример плохой нормалки с большим количеством градиентов.
Почему градиенты — это плохо? Текстуры — самая затратная часть любой игры. Когда вы качаете игру на 150 гигов, 100 из них — текстуры. Но все они компрессируются, что ухудшает их качество. Лучше всего компрессия видна в тех местах, где есть градиенты. Поэтому при максимально ровной нормалке ошибок быть не должно. Они, конечно, будут, но значительно меньше, чем с градиентом.
Следуя этим правилам, вы легко повысите качество своих работ, сократив усилия, необходимые для их создания.
Во второй части статьи рассмотрим основные правила хорошего текстурирования, которые помогут оживить вашу модель и сделать ее более интересной.
Туториал по Unreal Engine: Cel Shading
Благодаря физически точному рендерингу в Unreal Engine 4 удобно разрабатывать реалистичные игры. Модель рендеринга имитирует взаимодействие света с материалами, что приводит к созданию реалистичной картинки. Однако если вы хотите разработать игру со стилизованным внешним видом, то вам придётся исследовать другие техники.
Один из способов создания стилизации — использование cel shading (также известного как toon-шейдинг). Эта техника подражает затенению, обычно используемому в мультфильмах и аниме. Примеры её использования можно увидеть в таких играх, как Jet Set Radio, The Legend of Zelda: The Wind Waker и Gravity Rush.
В этом туториале вы научитесь следующему:
Примечание: В этом туториале подразумевается, что вы уже знакомы с основами Unreal Engine. Если вы новичок в Unreal Engine, то вам стоит изучить сначала туториал из десяти частей Unreal Engine для начинающих.
Приступаем к работе
Скачайте заготовку проекта и распакуйте её. Перейдите в папку проекта и откройте CelShader.uproject. Вы увидите следующую сцену:
Это персонаж, к которому мы будем применять cel shading. Прежде чем приступать, нужно разобраться, что же такое cel shading.
Что такое Cel Shading?
Cel shading — это процесс рендеринга с помощью нескольких полос цвета, а не непрерывного градиента.
Ниже представлен пример использования cel shading в The Legend of Zelda: Breath of the Wild. Заметьте, что cel shading реализован только для персонажа, а фон остался обычным.
На этом изображении есть три полосы: одна для теней, одна для средних тонов, и ещё одна для светлых участков.
Часто ошибочно считается, что cel shading применён, если у объектов есть контуры. Примером этого может служить Borderlands. Хотя эта игра имеет стилизованный внешний вид, на самом деле в ней нет cel shading. Это можно увидеть на изображении ниже. Заметьте, что в расцветке персонажа не используются полосы цвета.
Контуры не являются cel shading, но их часто используют совместно. Благодаря этому картинка становится похожей на нарисованную краской или тушью. Такой приём часто используется в играх с аниме-стилистикой, таких как Guilty Gear Xrd и Dragon Ball FighterZ.
В следующем разделе мы узнаем, как реализовать cel shading.
Способ реализации Cel Shading
Ограничение этого способа заключается в том, что на объекты с cel-шейдингом не могут влиять другие источники освещения. Кроме того, объекты не могут отбрасывать тени на объекты с cel-шейдингом.
Чтобы решить эту проблему, мы должны использовать другой способ. Вместо вычисления скалярного произведения мы будем вычислять освещённость поверхности. Затем при задании пороговых значений можно будет использовать это значение вместо скалярного произведения.
Теперь, когда вы знаете, что такое cel-шейдер и как он работает, настало время его создать.
Создание Cel-шейдера
В этом туториале cel shading будет эффектом постобработки. Постобработка (Post processing) позволяет изменять изображение после того, как движок закончит его рендеринг. Постобработку обычно используют для таких эффектов, как глубина резкости, motion blur и bloom.
Для создания собственного эффекта постобработки нам нужно воспользоваться материалом постобработки (post process material). Перейдите в папку Materials и создайте новый Material. Переименуйте его в PP_CelShader и откройте.
Чтобы преобразовать материал в материал постобработки, нужно изменить его домен (domain). Перейдите в панель Details и измените Material Domain на Post Process.
Первый шаг в создании cel-шейдера — вычисление освещённости каждого пикселя. Мы назовём это буфером освещения (lighting buffer).
Вычисление буфера освещения
Когда Unreal рендерит изображение на экран, то сохраняет проходы в буферы. Для вычисления буфера освещения нам нужно будет получить доступ к двум таким буферам:
Для доступа к этим буферам нужно использовать нод SceneTexture. Создайте его, выберите и перейдите в панель Details. Чтобы получить доступ к буферу Post Process Input, измените Scene Texture Id на PostProcessInput0.
Для доступа к Diffuse Color создайте ещё один нод SceneTexture. Измените его Scene Texture Id на DiffuseColor.
Буфер освещения должен содержать только значения в градациях серого (описывающие степени освещённости пикселей). Это значит, что нам не потребуется информация о цвете из обоих буферов. Чтобы отбросить цвета, соедините выход Color обоих нодов SceneTexture с Desaturation. Это полностью обесцветит оба буфера.
Для вычисления буфера освещения просто разделим SceneTexture:PostProcessInput0 на SceneTexture:DiffuseColor. Порядок здесь важен!
Затем используем Clamp, чтобы значения оставались в интервале от 0 до 1. Это упростит создание порогов, потому что мы будем знать возможные значения.
Вот визуализация буфера освещения:
Как вы видите, освещённые области ближе к белому, а неосвещённые — к чёрному.
Затем мы используем буфер освещения для создания порога.
Создание порога
В нашем cel-шейдере любой пиксель со значением больше 0,5 будет использовать обычный диффузный цвет. Пиксели со значениями меньше 0,5 будут использовать диффузный цвет половинной яркости.
Для начала создадим нод If. Он позволит нам сравнивать два значения. В зависимости от результатов сравнения мы сможем указывать разные выходы.
Далее соединим Clamp со входом A. Затем создадим Constant со значением 0.5 и соединим её со входом B.
Примечание: для изменения порога можно менять значение входа B.
Чтобы получить цвета, создадим SceneTexture и зададим для его Scene Texture Id значение Diffuse Color. Затем умножим Color на 0.5, чтобы получить диффузный цвет половинной яркости.
И наконец, соединим всё следующим образом:
Использование материалов постобработки
Чтобы использовать материалы постобработки, нам нужно создать Post Process Volume. Он обычно используется для управления такими эффектами постобработки, как баланс белого, насыщенность и контрастность.
Нажмите на Apply и вернитесь в основной редактор. Для создания Post Process Volume перейдите в панель Modes и выберите категорию Volumes. Затем перетащите Post Process Volume во Viewport, чтобы создать его.
Теперь нам нужно сказать Post Process Volume, чтобы он использовал cel-шейдер. Выбрав Post Process Volume, перейдите в панель Details. Затем найдите Rendering Features\Post Process Materials и нажмите на значок +. Так вы добавите в массив новый элемент.
Затем нажмите на раскрывающийся список Choose и выберите Asset Reference.
Это позволит выбрать материал. Нажмите на раскрывающийся список None и выберите PP_CelShader.
По умолчанию Post Process Volume воздействует только тогда, когда мы находимся внутри. Однако в нашем случае нужно, чтобы он влиял на весь мир. Для этого прокрутите до Post Process Volume Settings и включите Infinite Extent (Unbound).
Теперь, когда cel-шейдер применён ко всей игре, мы увидим следующее:
«Постойте-ка, это не похоже на тот cel-шейдер, который вы показывали раньше!»
Главная причина такого отличия в том, что движок применяет cel-шейдер после тональной компрессии. Чтобы исправить это, нам нужно попросить движок использовать cel-шейдер перед тональной компрессией.
Применение Cel Shading перед тональной компрессией
Прежде чем показать изображение игроку, Unreal выполняет процесс, известный как «тональная компрессия» (tonemapping). Одна из целей тональной компрессии — сделать изображение более естественным. Она берёт входной цвет, а затем использует кривую, чтобы сдвинуть его в новое значение.
Вот два изображения, до и после тональной компрессии:
Как вы видите, светлые участки до тональной компрессии слишком яркие. Однако после тональной компрессии яркие области становятся более мягкими.
Хотя тональная компрессия полезна для изображений, которые нужно отображать, мы не должны выполнять тональную компрессию для изображений, которые хотим использовать в вычислениях. Из-за смещения значений мы будем использовать не те значения, которые ожидаем.
Откройте PP_CelShader и убедитесь, что ничего не выбрано. Затем зайдите в панель и найдите раздел Post Process Material. Задайте Blendable Location значение Before Tonemapping.
Нажмите на Apply, а затем вернитесь в основной редактор. Цвета теперь выглядят гораздо лучше!
В следующем разделе мы научимся тому, как применять cel shading только к отдельным объектам.
Изолирование Cel Shader
Чтобы изолировать эффекты постобработки, нам нужно использовать функцию под названием Custom Depth. Как и Post Process Input с Diffuse Color, это тоже буфер, который можно использовать в материалах постобработки.
Прежде чем понять, что такое Custom Depth, вам нужно разобраться с буфером Scene Depth. Scene Depth хранит отдалённость каждого пикселя от камеры. Вот как выглядит визуализация Scene Depth:
Custom Depth хранит ту же информацию, но только для выбранных вами мешей. Вот его визуализация с викингом, отрендеренным в Custom Depth:
Сравнивая Scene Depth с Custom Depth, мы можем изолировать объекты. Если Scene Depth меньше, чем Custom Depth, то мы используем обычное изображение. Если Scene Depth больше Custom Depth, то используется изображение с cel-шейдингом.
Первый шаг — рендеринг викинга в Custom Depth.
Использование Custom Depth
Перейдите в World Outliner и выберите SK_Viking. Затем перейдите в панель Details и найдите раздел Rendering. Затем включите Render CustomDepth Pass.
Далее нам нужно выполнить сравнение глубин. Откройте PP_CelShader и создайте следующую схему:
Примечание: ноды Mask ( R ) являются масками компонентов (Component Mask). Они позволяют преобразовывать многоканальные данные в скалярные значения. Наложить маску на Scene Depth и Custom Depth нам нужно потому, что нод If для входов A и B принимает только скалярные значения.
Затем соедините выход сети cel shading с A > B. Наконец, соедините выход только что созданного If с Emissive Color.
Теперь cel shading будет применяться только к мешам, отрендеренным в Custom Depth.
Нажмите на Apply, а затем вернитесь в основной редактор. Вы увидите, что cel shading теперь выполняется только для викинга.
Сel-шейдер работает замечательно, но он довольно прост. Что, если нам понадобится большее количество полос? Что, если мы захотим создать более плавные переходы между полосами? Всё это можно реализовать с помощью таблиц поиска (lookup tables) (LUT).
Что такое «таблица поиска»?
В детстве мы учились тому, что такое умножение. Однако юный мозг не всегда мог выполнять такие вычисления. Поэтому вместо расчётов вы могли пользоваться таблицей умножения для «поиска» ответов.
По сути, это и есть LUT. Это массив значений (обычно предварительно вычисленных), к которым можно получить доступ с помощью входных данных. В случае таблицы умножения входными данными являлись множитель и множимое.
В контексте нашего cel-шейдера LUT — это текстура с неким градиентом. Вот четыре примера того, как может выглядеть LUT:
Пока мы вычисляем цвет тени, умножая диффузный цвет на 0,5. Вместо умножения на постоянную 0,5 мы будем использовать значение из LUT. Благодаря этому мы можем управлять количеством полос и их переходами. Понять, как будет выглядеть затенение, можно по внешнему виду LUT.
Прежде чем использовать LUT, нужно изменить некоторые из её параметров текстуры.
Изменение параметров LUT
Перейдите в папку Textures и откройте T_Lut_01. Вот, как выглядит LUT:
Первый параметр, который нам нужно изменить — это sRGB. При рендеринге Unreal преобразует все текстуры со включенным sRGB в линейный цвет. Это упрощает движку выполнение вычислений рендеринга.
Параметр sRGB полезен для текстур, описывающих внешний вид. Однако текстуры наподобие карт нормалей и LUT содержат данные, предназначенные для математических вычислений. Поэтому Unreal должен считать их значения уже верными. Если отключить sRGB, то Unreal не будет выполнять преобразование в линейный цвет.
Для этого снимите флажок sRGB. Этот параметр находится в разделе Texture.
Следующий параметр, который нам нужно изменить — способ тайлинга текстуры. Поскольку мы не будем отображать эту текстуру, то тайлинг ей не нужен. Более того, если оставить тайлинг включённым, то это добавит проблем при сэмплировании на краях текстуры. Например, если мы будем сэмплировать пиксель с левого края, то из-за тайлинга он попытается смешаться с правым краем.
Для отключения тайлинга измените значение X-axis Tiling Method на Clamp. Тоже самое сделайте для Y-axis Tiling Method.
И на этом мы закончили с параметрами. Теперь нам нужно использовать LUT в материале постобработки.
Использование LUT
Закройте T_Lut_01 и откройте PP_CelShader. Сначала удалите выделенные ноды:
Затем создайте Texture Sample и измените его Texture на T_Lut_01. Эта таблица LUT будет создавать три полосы с плавным переходом.
Как мы помним, для определения значений, которые нужно выводить, LUT используют входные данные. В нашем случае в качестве входных данных будет использоваться буфер освещения.
Чтобы реализовать это, соедините Clamp с UVs в Texture Sample.
Это работает, потому что значения буфера освещения и координаты текстуры находятся в интервале от 0 и 1. Например, если пиксель из буфера освещения равен 0,5, то LUT подаст на выход значение пикселя из середины текстуры.
Далее нам нужно умножить диффузный цвет на LUT. Для этого воссоздайте следующую схему:
Мы используем Append для преобразования вывода Texture Sample’s в четырёхканальный вектор. Это нужно нам, потому что мы не можем умножать трёхканальный вектор на четырёхканальный (SceneTexture).
Наконец, соединим всё следующим образом:
Теперь вместо умножения диффузного цвета на константу мы умножаем его на значение из LUT. Так мы контролируем количество полос цвета и их переходы (зависящие от LUT). Выводимое значение LUT определяется буфером освещения.
Нажмите на Apply, а затем закройте PP_CelShader. Теперь затенение будет иметь три полосы с более плавными переходами между полосами.
Ниже представлено сравнение того, как могут выглядеть различные LUT. Эти LUT тоже добавлены в проект.
Куда двигаться дальше?
Готовый проект можно скачать отсюда.
Как вы видите, материалы постобработки — очень мощный инструмент. Они позволяют создавать множество реалистичных и стилизованных эффектов. Если вы хотите больше узнать о постобработке, то изучите документацию по постобработке UE.