В этом туториале по Unreal Engine 4 вы научитесь рисовать цветами по мешу любого типа.
Рисование по мешу позволяет игрокам раскрашивать внутриигровые объекты. Примеры рисования по мешу: граффити (goop) в Super Mario Sunshine, гели в Portal 2 и чернила в Splatoon. Рисование может использоваться как геймплейний элемент или быть просто графическим оформлением. Как бы то ни было, рисование по мешу открывает новые возможности для дизайнеров геймплея и художников.
Хотя в перечисленных выше примерах этот эффект используется почти одинаково, вы можете применять рисование по мешу и для многих других эффектов. Например, можно рисовать на объектах банками с краской, прорисовывать на персонажах раны или даже позволить игрокам самим нарисовать лицо персонажа!
В этом туториале вы узнаете, как рисовать на скелетном меше. Для этого мы сделаем следующе:
- Развернём меш в его UV-форму
- Используем точку пересечения трассировки линии для создания сферической маски меша
- Отрендерим развёрнутый меш и сферическую маску в render target с помощью захвата сцены (scene capture)
- Используем маску для смешивания текстур в материале персонажа
Учтите, что этот туториал не посвящён рисованию вершин. Рисование вершин зависит от разрешения меша и его невозможно изменять в процессе игры. Используемый в этом туториале метод, напротив, работает вне зависимости от разрешения меша и его можно применять во время игры.
Примечание: подразумевается, что вы уже знаете основы работы с Unreal Engine. Если вы новичок в Unreal Engine, то изучите нашу серию туториалов из 10 частей Unreal Engine для начинающих.
Примечание: этот туториал является четвёртой частью серии туториалов об использовании render targets в Unreal Engine:
- Часть 1: рисование с помощью Render Targets
- Часть 2: деформируемый снег
- Часть 3: интерактивная трава
- Часть 4: рисование по мешу
Приступаем к работе
Начните со скачивания материалов для этого туториала. Распакуйте их, перейдите в MeshPainterStarter и откройте MeshPainter.uproject. Нажмите Play, чтобы увидеть персонажа, которого мы будем раскрашивать.
Как и в туториалах о снеге и траве, в этом способе тоже нужен захват сцены (scene capture). Для экономии времени я уже создал блюпринт захвата сцены. Если вы хотите больше узнать о параметрах захвата, то прочитайте наш туториал о создании следов на снегу.
Для начала давайте узнаем, как можно рисовать на меше.
Рисование по мешу
В большинстве случаев меши, с которыми вам предстоит работать, уже имеют UV-развёртку. Поэтому логично будет создать в помощью render target маску, а затем применить её к мешу. Однако генерирование маски непосредственно на render target (с помощью Draw Material to Render Target) обычно приводит к разрывам в UV shells.
Вот пример UV-развёртки куба и текстуры сферической маски:
А вот применённая к кубу маска:
Как видите 2D-мерная сферическая маска не огибает углы и не учитывает геометрию. Для генерации правильной маски сферическая маска должна сэмплировать позиции в мире. Как же получить доступ к позициям в мире при использовании нода Draw Material to Render Target?
Если вы изучали методы рисования по мешам, то, возможно, находили видео Райана Брука о рисовании повреждений персонажа с помощью render targets (используемый в моём туториале метод основан на его методе). В видео он успешно генерирует трёхмерные сферические маски и накапливает их в render target. Ему удалось это сделать, потому что он создаёт render target для хранения позиций меша в мире, которые затем можно сэмплировать с использованием сферической маски. Давайте рассмотрим этот метод подробнее.
Метод Райана
Этот метод состоит из четырёх этапов. Первый этап заключается в «разворачивании» нужного меша. Нужно просто переместить все вершины так, чтобы получить меш в его UV-форме. Например, вот UV-координаты персонажа:
А вот персонаж после разворачивания в Unreal:
Развернуть меш можно, применив простые вычисления смещений позиций мира (которые мы рассмотрим ниже).
Второй этап заключается в кодировании позиций в мире в render target. Это можно сделать, задав цвету материала развёртки значение Absolute World Position и воспользовавшись scene capture для захвата развёртки. Вот как будет выглядеть render target:
Примечание: render target меняет цвет, потому что персонаж анимирован. Это приводит к постоянному изменению позиций в мире.
Третий этап заключается в создании сферических масок. Получив доступ к позициям меша в мире, мы можем сэмплировать их на сферическую маску, а затем рисовать сферическую маску непосредственно на втором render target.
Последний этап — применение маски к материалу персонажа для смешивания цветов, текстур или материалов. Вот визуализация третьего и четвёртого этапов:
Теперь давайте рассмотрим предлагаемый мной метод.
Предлагаемый метод
Хоть метод Райана и работает, для него нужно следующее:
- Две отрисовки render target. Первая захватывает неразвёрнутый меш, а вторая накапливает сферические маски.
- Один render target для хранения позиций в мире
- Render target для накопления сферических масок. Для каждого актора, на котором нужно рисовать, вам потребуется отдельный render target.
Рассматриваемый в этом туториале метод отказывается от второй отрисовки и render target позиции в мире. Это возможно благодаря комбинированию развёртки и сферических масок в один материал (материал развёртки). После чего выполняется захват развёртки с помощью аддитивного композитного режима для накопления сферических масок.
Стоит заметить, что оптимальнее всего оба метода работают, когда у меша нет пересекающихся UV. Если UV накладываются друг на друга, то пиксели имеют общее UV-пространство, а значит и одинаковую информацию о масках. Например, обе руки персонажа могут быть UV-развёрнуты в одном пространстве. Если на одну руку накладывается маска, то и на другую она тоже наложится.
Теперь, когда мы познакомились с методом, давайте начнём с создания материала развёртки.
Создание материала развёртки
Перейдите в папку Materials и создайте новый материал. Назовите его M_Unwrap, а затем откройте.
Измените следующие параметры:
- Shading Model: Unlit. Благодаря этому захват сцены не будет захватывать информацию об освещении.
- Two Sided: Enabled. Иногда развёрнутые грани могут смотреть в другую сторону (это зависит от того, как была выполнена UV-развёртка меша). Параметр Two Sided гарантирует, что мы увидим все перевёрнутые грани.
- Usage\Used with Skeletal Mesh: Enabled. При включении этого параметра будут скомпилированы шейдеры, необходимые для того, чтобы материал работал на скелетных мешах.
Далее мы развернём меш. Для этого создайте показанную ниже схему. Учтите, что я уже создал параметры CaptureSize и UnwrapLocation в ассете MPC_Global.
Так мы выполним UV-развёртку меша в указанное место и с указанным размером. Учтите, что если уникальная UV-развёртка вашего меша находится в отдельном канале, то необходимо будет изменить Coordinate Index нода TextureCoordinate. Например, если уникальные UV-координаты находятся в канале 1, то Coordinate Index необходимо присвоить значение 1.
Следующий этап — это создание сферической маски. Для этого нам необходимы два параметра: точка пересечения и радиус сферы. Создайте выделенные ноды:
Эта схема будет возвращать белый цвет для пикселей внутри сферической маски и чёрный цвет для пикселей за её пределами. Не беспокойтесь о задании значения для параметров, потому что мы займёмся этим в блюпринтах.
Важно задать для нода Absolute World Position значение Absolute World Position (Excluding Material Shader Offsets). Это необходимо, потому что позиция пикселя в мире будет из-за разворачивания изменяться. Excluding Material Shader Offsets предоставляет нам перед разворачиванием исходную позицию в мире.
И это всё, что нужно для материала развёртки. Нажмите на Apply и закройте материал. Далее нам нужно применить материал к персонажу, чтобы развернуть его.
Разворачивание персонажа
В этом туториале разворачиванием и захватом будет заниматься блюпринт захвата. Для начала нам понадобится динамический экземпляр материала развёртки. Перейдите в папку Blueprints и откройте BP_Capture. Затем добавьте к Event BeginPlay выделенные ноды. Убедитесь, что в качестве Parent задано M_Unwrap.
Далее нам потребуется функция для выполнения развёртки и захвата. Создайте новую функцию под названием PaintActor. Затем создайте следующие входы:
- ActorToPaint: тип должен иметь значение Actor. Это актор, для которого мы будем выполнять развёртку и захват.
- HitLocation: тип Vector. Это будет центральная точка сферической маски.
- BrushRadius: тип Float. Радиус сферической маски в единицах измерения мира.
Хотя этот метод рисования может работать с любым актором, мы будем только проверять, наследуется ли получаемый актор от класса Character. И для упрощения кода мы будем хранить компонент скелетного меша в переменной, потому что нам несколько раз придётся ссылаться на него. Для этого мы добавим выделенные ноды:
Теперь настало время заняться развёрткой и наложением сферической маски. Для этого добавим в конец цепочки нодов выделенные ноды:
Вот что делает каждая из строк:
- Во-первых, мы сохраняем исходный материал меша, чтобы заново применить его позже. Затем мы применяем материал развёртки.
- Эта строка передаёт материалу развёртки точку пересечения и радиус кисти для применения сферической маски
Чтобы протестировать развёртку, нам сначала нужно выполнить трассировку линии от игрока, чтобы получить точку пересечения.
Получение точки пересечения
Нажмите Compile и вернитесь в основной редактор. Затем закройте BP_Player. Откройте функцию Shoot и добавьте выделенные ноды. Для этого туториала задайте для Brush Radius значение 10.
Нажмите на Compile и закройте BP_Player. Нажмите Play, а затем щёлкните левой клавишей мыши на персонаже, чтобы выполнить развёртку и применить сферическую маску.
Если вы не понимаете, почему маска продолжает двигаться, то это потому, что части двигаются относительно сферической маски. Однако это не проблема, потому что мы выполняем захват развёртки только в момент пересечения.
Теперь, когда мы развернули меш, нам нужно выполнить захват развёртки.
Захват развёртки
Для начала неплохо было бы добавить неосвещённую чёрную плоскость за развёрнутым мешем. Это позволит избежать швов на ребре UV shells. Откройте BP_Capture, а затем добавьте компонент Plane под названием BackgroundPlane. Для экономии времени я уже создал чёрный материал. В качестве материала выберите M_Background.
В этом туториале мы используем для развёртки и захвата размер в 500×500 единиц, поэтому фоновая плоскость должна быть не меньше этих размеров. Задайте для Scale значения (5.0, 5.0, 1.0).
Поскольку позиция плоскости и позиция развёртки совпадают, то неплохо будет сместить плоскость вниз, чтобы избежать z-конфликтов. Для этого зададим для Location значения (0.0, 0.0, -1.0).
Далее нам нужно выполнить захват. Вернитесь к функции PaintActor и добавьте выделенные ноды:
Так мы выполним захват развёрнутого меша, после чего снова применится исходный материал меша.
В получившейся схеме захват сцены переписывает содержимое render target. Чтобы сферические маски накапливались, нам нужно сделать так, чтобы захват сцены прибавлял к предыдущему содержимому. Для этого выберите компонент SceneCapture и задайте для Scene Capture\Composite Mode значение Additive.
Нажмите на Compile, а затем закройте блюпринт. Теперь нам нужно использовать render target в материале персонажа.
Использование маски
Перейдите к Characters\Mannequin\Materials и откройте M_Mannequin. Затем добавьте выделенные ноды. Задайте для Texture Sample значение RT_Capture.
Эта схема будет отображать красный цвет там, где маска белая, и оранжевый там, где маска чёрная. Однако мы можем сделать так, чтобы текстуры или слои материалов смешивались.
Нажмите на Apply и закройте материал. Нажмите Play и щёлкните левой клавишей мыши на персонаже, чтобы начать рисование.
Куда двигаться дальше?
Готовый проект можно скачать отсюда.
Хотя в этом туториале мы использовали сферические маски, это не единственная фигура, которую можно применять. Ведь есть кубы, цилиндры, конусы и многое другое! Чтобы больше узнать об этих формах фигурах и способах их применения, можно прочитать следующие два поста:
- Modelling with Distance Functions Иниго Килеза
- Volumetric Rendering: Signed Distance Functions Алана Зуккони
Если вы хотите изучить ещё один способ рисования по мешу, то прочитайте статью Тома Лумана Rendering Wounds on Characters. Вместо накапливания сферических масок он использует фиксированное количество сферических масок. Преимущество этого метода заключается в дешевизне (которая зависит от количества сферических масок) и в относительной простоте анимирования масок. Недостаток метода заключается в жёстком ограничении на количество сферических масок.