![](https://habrastorage.org/getpro/habr/post_images/276/6ee/ee4/2766eeee4fcc87c31919d427c829ee98.gif)
Render target — это, по сути, текстура, в которую можно выполнять запись во время выполнения приложения. С точки зрения движка они хранят такую информацию, как базовый цвет, нормали и ambient occlusion.
С точки зрения пользователя render target в основном используются в качестве своего рода дополнительной камеры. Можно задать захват сцены (scene capture) в какой-то точке и сохранять изображение в render target. Затем можно отобразить render target на меше, например, для симуляции камеры наблюдения.
После выпуска версии движка 4.13 компания Epic добавила возможность отрисовки материалов непосредственно на render target с помощью блюпринтов. Эта функция позволяет создавать сложные эффекты, например, симуляцию жидкости и деформацию снега. Звучит потрясающе, правда? Но прежде чем переходить к таким сложным эффектам, лучше всего освоиться с чем-то простым. Что может быть проще, чем рисование на render target?
В этом туториале вы научитесь следующему:
- Динамически создавать render target с помощью блюпринтов
- Отображать render target на меше
- Рисовать текстуру на render target
- Менять размер кисти и текстуру во время игрового процесса
Примечание: в этом туториале подразумевается, что вы уже знакомы с основами работы в Unreal Engine. Если вы новичок в Unreal Engine, то изучите нашу серию туториалов из десяти частей Unreal Engine для начинающих.
Приступаем к работе
Начнём с загрузки материалов для этого туториала (взять их можно здесь). Распакуйте их, перейдите к CanvasPainterStarter и откройте CanvasPainter.uproject. Если вы нажмёте Play, то увидите следующее:
![](https://habrastorage.org/getpro/habr/post_images/9b9/e6d/01f/9b9e6d01f6955d5a93f737588f4bb730.jpg)
Квадрат посередине (canvas) — это то, на чём мы будем рисовать. Элементы UI слева будут текстурой, которой мы будем рисовать, и её размером.
Для начала давайте разберёмся со способом, который используется для рисования.
Способ рисования
Первое, что нам нужно — это render target, используемый в качестве холста (canvas). Для определения того, где выполнять рисование на render target, мы оттрассируем прямую, выходящую из камеры вперёд. Если прямая пересекает холст, то мы можем получить место пересечения в UV-пространстве.
Например, если холст имеет идеальную привязку UV-координат, то пересечение в центре вернёт значение (0.5, 0.5). Если прямая пересекает холст в правом нижнем углу, то мы получим значение (1, 1). Затем можно использовать простые вычисления для расчёта места рисования.
![](https://habrastorage.org/getpro/habr/post_images/5f2/369/fe2/5f2369fe21d9655fa5e7fc0914c6c023.gif)
Но зачем получать координаты в UV-пространстве? Почему бы не использовать координаты настоящего пространства мира? При использовании пространства мира нам сначала придётся вычислять место пересечения относительно плоскости. Также придётся учитывать поворот и масштаб плоскости.
При использовании UV-пространства все эти вычисления не требуются. На плоскости с идеальной привязкой UV-координат пересечение с серединой всегда возвращает (0.5, 0.5), вне зависимости от расположения и поворота плоскости.
Примечание: рассматриваемый в этом туториале метод в общем случае работает только с плоскостями или похожими на плоскости поверхностями. Для других типов геометрии требуется более сложный метод, который я рассмотрю в другом туториале.
Сначала мы создадим материал, который будет отображать render target.
Создание материала холста
Перейдите в папку Materials и откройте M_Canvas.
В этом туториале мы будем создавать render target динамически, с помощью блюпринтов. Это значит, что нам придётся настроить текстуру как параметр, чтобы можно было передавать его render target. Для этого создадим TextureSampleParameter2D и назовём его RenderTarget. Затем соединим его с BaseColor.
![](https://habrastorage.org/getpro/habr/post_images/439/741/68a/43974168a064c39f35acc53ab09f1c65.jpg)
Пока не беспокойтесь о выборе текстуры — мы займёмся этим дальше в блюпринтах. Нажмите Apply, а затем закройте M_Canvas.
Следующим этапом будет создание render target, после чего мы используем его в качестве материала холста.
Создание Render Target
Существует два способа создания render target. Первый: создание в редакторе нажатием на Add New\Materials & Textures\Render Target. Этот способ позволяет удобно ссылаться на один и тот же render target нескольким акторам. Однако если нам понадобятся несколько холстов, то придётся создавать render target вручную для каждого холста.
Поэтому лучше создавать render target с помощью блюпринтов. Преимущество такого подхода в том, что мы создаём render targets только при необходимости и они не раздувают объём файлов проекта.
Для начала нам нужно создать render target и сохранить его как переменную для дальнейшего использования. Перейдите в папку Blueprints и откройте BP_Canvas. Найдите Event BeginPlay и добавьте выделенные узлы.
![](https://habrastorage.org/getpro/habr/post_images/7ed/91a/e85/7ed91ae8543ccf5875dad7828c308ed1.jpg)
Присвойте параметрам Width и Height значение 1024. Так мы изменим разрешение render target на 1024×1024. Чем больше значения, тем выше качество изображения, но и больше затраты видеопамяти.
![](https://habrastorage.org/getpro/habr/post_images/aeb/56f/96c/aeb56f96c38d3ae2606d4f3b2d3abeaf.jpg)
Далее идёт нод Clear Render Target 2D. Мы можем использовать этот нод для задания цвета render target. Задайте Clear Color значение (0.07, 0.13, 0.06). При этом весь render target заполнится зеленоватым цветом.
![](https://habrastorage.org/getpro/habr/post_images/94b/d30/eed/94bd30eedc1f590ffd157c8091d7898e.jpg)
Теперь нам нужно отобразить render target на меше холста.
Отображение Render Target
На данном этапе меш холста использует материал по умолчанию. Для отображения render target нужно создать динамический экземпляр M_Canvas и передать ему render target. Затем нужно применить динамический экземпляр материала к мешу холста. Для этого мы добавим выделенные ноды:
![](https://habrastorage.org/getpro/habr/post_images/c9a/7d8/1aa/c9a7d81aa770c63cc6cf75f52746c4c3.jpg)
Сначала перейдём к ноду Create Dynamic Material Instance и зададим в качестве Parent значение M_Canvas. Так мы создадим динамический экземпляр M_Canvas.
![](https://habrastorage.org/getpro/habr/post_images/da7/f26/2bf/da7f262bfda0215098287d3a2286085b.jpg)
Далее перейдём к ноду Set Texture Parameter Value и зададим для Parameter Name значение RenderTarget. Так мы передадим render target созданному ранее параметру текстуры.
![](https://habrastorage.org/getpro/habr/post_images/7c7/8d0/377/7c78d0377ef6a592c05f5cd54d08a89e.jpg)
Теперь на меше холста будет отображаться render target. Нажмите на Compile и вернитесь в основной редактор. Нажмите на Play, чтобы увидеть, как холст изменит цвет.
![](https://habrastorage.org/getpro/habr/post_images/53f/949/fd6/53f949fd6ad350b4a016545af0eca47c.jpg)
Теперь, когда у нас есть холст, нам нужно создать материал, который можно использовать в качестве кисти.
Создание материала кисти
Перейдите в папку Materials. Создайте материал M_Brush и откройте его. Сначала задайте для Blend Mode значение Translucent. Это позволит нам использовать текстуры с прозрачностью.
![](https://habrastorage.org/getpro/habr/post_images/cf2/18a/4b7/cf218a4b7b0e2c57f307cd06d8a92860.jpg)
Как и в случае с материалом холста, мы задаём текстуру для кисти в блюпринтах. Создайте TextureSampleParameter2D и назовите его BrushTexture. Соедините его следующим образом:
![](https://habrastorage.org/getpro/habr/post_images/097/c49/7d1/097c497d1e50af95c004f9bdfe849897.jpg)
Нажмите на Apply, а затем закройте M_Brush.
Следующее, что нужно сделать — создать динамический экземпляр материала кисти, чтобы можно было менять текстуру кисти. Откройте BP_Canvas и добавьте выделенные узлы.
![](https://habrastorage.org/getpro/habr/post_images/49d/628/860/49d62886060349fc146ce04c150d539c.jpg)
Далее перейдите в нод Create Dynamic Material Instance и задайте для Parent значение M_Canvas.
![](https://habrastorage.org/getpro/habr/post_images/8e0/009/4aa/8e00094aa61fbd27c527f207b360856b.jpg)
Мы создали материал кисти, и теперь нам нужна функция для рисования кистью на render target.
Рисование кистью на Render Target
Создайте новую функцию и назовите её DrawBrush. Сначала нам понадобятся параметры: используемая текстура, размер кисти и место для рисования. Создайте следующие входные входные данные:
- BrushTexture: выберите тип Texture 2D
- BrushSize: выберите тип float
- DrawLocation: выберите тип Vector 2D
![](https://habrastorage.org/getpro/habr/post_images/e6e/2cb/d2f/e6e2cbd2f9e69222d7bcbe616d30f8b2.jpg)
Прежде чем рисовать кисть, нам нужно задать её текстуру. Для этого создадим схему, показанную ниже. Убедитесь, что в качестве Parameter Name выбрано значение BrushTexture.
![](https://habrastorage.org/getpro/habr/post_images/0ff/645/efc/0ff645efc2374126884a93b9b3a8c25b.jpg)
Теперь нам нужно выполнять отрисовку в render target. Для этого создадим выделенные ноды:
![](https://habrastorage.org/getpro/habr/post_images/c69/b73/c02/c69b73c027dc7280673644d512498492.jpg)
Begin Draw Canvas to Render Target позволит движку узнать, что мы хотим начать отрисовку в определённый render target. Draw Material позволит отрисовывать материал по указанному местоположению с выбранным размером и поворотом.
Вычисление позиции отрисовки — это двухэтапный процесс. Сначала нам нужно отмасштабировать DrawLocation, чтобы уместиться в разрешение render target. Для этого умножим DrawLocation на Size.
![](https://habrastorage.org/getpro/habr/post_images/cbb/513/4d2/cbb5134d2311305c42095e53939cc130.jpg)
По умолчанию движок будет отрисовывать материалы, используя в качестве исходной точки верхний левый угол. Поэтому текстура кисти не будет центрирована нам, где мы хотим выполнить отрисовку. Чтобы исправить это, нам нужно поделить BrushSize на 2, а затем вычесть результат из предыдущего этапа.
![](https://habrastorage.org/getpro/habr/post_images/501/58f/e82/50158fe822073487d12a3099f86c7737.jpg)
Затем соединим всё следующим образом:
![](https://habrastorage.org/getpro/habr/post_images/d58/20a/111/d5820a1117491a78e4e020c4fd13e6ca.jpg)
Наконец нам нужно сообщить движку, что мы хотим остановить отрисовку в render target. Добавим нод End Draw Canvas to Render Target и соединим его следующим образом:
![](https://habrastorage.org/getpro/habr/post_images/86f/8ff/a3e/86f8ffa3ecad0a4b3b47fe21f4044eb0.jpg)
Теперь при каждом выполнении DrawBrush она будет сначала устанавливать в качестве текстуры для BrushMaterial передаваемую текстуру. Затем она будет отрисовывать BrushMaterial в RenderTarget, пользуясь переданными позицией и размером.
И на этом функция отрисовки готова. Нажмите на Compile и закройте BP_Canvas. Следующим шагом будет трассировка прямой из камеры и рисование в том месте холста, где произошло пересечение.
Трассировка прямой из камеры
Прежде чем рисовать на холсте, нам нужно указать текстуру кисти и размер. Перейдём в папку Blueprints и откроем BP_Player. Затем присвоим переменной BrushTexture значение T_Brush_01, а переменной BrushSize значение 500. Так мы назначим кисти изображение обезьяны размером 500×500 пикселей.
![](https://habrastorage.org/getpro/habr/post_images/a76/ab2/b26/a76ab2b267bd2f0ea514b319528eeb26.jpg)
Далее необходимо выполнить трассировку прямой. Найдите InputAxis Paint и создайте следующую схему:
![](https://habrastorage.org/getpro/habr/post_images/775/cf0/68e/775cf068e5c0820fa2d4539ca6ae3960.jpg)
Так мы будем выполнять трассировку прямой, направленной из камеры прямо, пока игрок будет удерживать клавишу, назначенную для Paint (в нашем случае это левая клавиша мыши).
Теперь нам необходимо проверить, пересекла ли прямая холст. Добавим выделенные ноды:
![](https://habrastorage.org/getpro/habr/post_images/da5/f53/97b/da5f5397b6a59eebdb9fce09541a35d8.jpg)
Теперь при пересечении прямой и холста будет выполняться функция DrawBrush, использующая переданные ей переменные кисти и UV-координаты.
Чтобы нод Find Collision UV заработал, нам нужно изменить два параметра. Во-первых, перейдём в нод LineTraceByChannel и включим Trace Complex.
![](https://habrastorage.org/getpro/habr/post_images/759/207/f4a/759207f4a22689a21f91f6c244648f75.jpg)
Во-вторых, перейдём в Edit\Project Settings, а затем в Engine\Physics. Включим Support UV From Hit Results и перезапустим проект.
![](https://habrastorage.org/getpro/habr/post_images/c8f/42d/56b/c8f42d56bac2b1ec4ad15884efa7f51d.jpg)
После перезапуска для рисования на холсте нажмите Play и левую клавишу мыши.
![](https://habrastorage.org/getpro/habr/post_images/2b3/5ff/ad6/2b35ffad6680fa6349018143397310f3.gif)
Можно даже создать несколько холстов и рисовать на каждом из них отдельно. Это возможно, потому что каждый холст динамически создаёт собственный render target.
![](https://habrastorage.org/getpro/habr/post_images/151/9c6/89e/1519c689ef59642b5fbb6a8dfbd304dc.gif)
В следующем разделе мы реализуем функционал изменения игроком размера кисти.
Изменение размера кисти
Откройте BP_Player и найдите нод InputAxis ChangeBrushSize. Эта привязка оси настроена на использование колеса мыши. Для изменения размера кисти нам достаточно менять значение BrushSize в зависимости от Axis Value. Для этого создадим следующую схему:
![](https://habrastorage.org/getpro/habr/post_images/554/da6/2c8/554da62c836912958e8588e582862a69.jpg)
Она будет выполнять прибавление или вычитание из BrushSize при использовании игроком колеса мыши. Первое умножение определяет скорость прибавления или вычитания. В качестве меры безопасности добавлено Clamp (float). Он гарантирует, что размер кисти не станет меньше 0 или больше 1000.
Нажмите на Compile и вернитесь в основной редактор. Покрутите колесо мыши, чтобы изменить размер кисти при рисовании.
![](https://habrastorage.org/getpro/habr/post_images/276/6ee/ee4/2766eeee4fcc87c31919d427c829ee98.gif)
В последнем разделе мы создадим функционал, позволяющий игроку менять текстуру кисти.
Изменение текстуры кисти
Для начала нам нужен массив для хранения текстур, которые может использовать игрок. Откройте BP_Player и создайте переменную array. Выберите для неё тип Texture 2D и назовите её Textures.
![](https://habrastorage.org/getpro/habr/post_images/425/45f/db9/42545fdb9060f05e84a1e251be28bc3e.jpg)
Затем создайте три элемента в Textures. Присвойте им следующие значения:
- T_Brush_01
- T_Brush_02
- T_Brush_03
![](https://habrastorage.org/getpro/habr/post_images/93e/a5a/9fb/93ea5a9fb103654bac3448131c9b6d11.jpg)
Это будут текстуры, которыми сможет рисовать игрок. Для добавления новых текстур достаточно добавить их в этот массив.
Далее нам необходима переменная для хранения текущего индекса массива. Создайте переменную integer и назовите её CurrentTextureIndex.
![](https://habrastorage.org/getpro/habr/post_images/c0f/b87/b82/c0fb87b8243042c1ce3a6d441490b63e.jpg)
Далее нам необходим способ обхода в цикле всех текстур. Для этого туториала я настроил привязку действия (action mapping) под названием NextTexture и привязал её к правой клавише мыши. Когда игрок нажимает эту клавишу, должен выполняться переход к следующей текстуре. Для этого найдите нод InputAction NextTexture и создайте следующую схему:
![](https://habrastorage.org/getpro/habr/post_images/3f2/3d1/9ad/3f23d19ad803a74af5a11892dc7e3579.jpg)
Эта схема будет увеличивать CurrentTextureIndex при каждом нажатии на правую клавишу мыши. Если индекс достигает конца массива, то он снова сбрасывается на 0. Наконец, BrushTexture задаётся соответствующая текстура.
Нажмите на Compile и закройте BP_Player. Нажмите Play и щёлкайте правой клавишей мыши для переключения между текстурами.
![](https://habrastorage.org/getpro/habr/post_images/8f4/ed0/d25/8f4ed0d25ed9f1944327137905ecba2b.gif)
Куда двигаться дальше?
Готовый проект можно скачать отсюда.
Render target — это чрезвычайно мощный инструмент и в этом туториале мы затронули только самые основы. Если вы хотите узнать, на что способны render targets, посмотрите Content-Driven Multipass Rendering in UE4. В этом видео есть примеры рисования карт потоков, объёмного рисования, симуляции жидкостей и многого другого.
Также посмотрите наше обучающее видео Blueprint Drawing to Render Targets, чтобы узнать, как создать с помощью render target рисовалку карты высот.