Хабрахабр

[Перевод] Unreal Engine4 — PostProcess эффект сканирования

автор на момент статьи получал степень магистра наук), и я решил вернуться к созданию шейдеров, придумав этот postprocess эффект сканирования. В эти выходные у меня появилось немного свободного времени между занятиями (прим. Также мы используем некоторые простые искажения шума, чтобы эффект выглядел немного интереснее. Я представил, что он используется в игре как своего рода эффект сканирования на расстоянии.

Существуют несколько способов, с помощью которых вы можете создать этот эффект. В этой статье я расскажу, как реализовать данный эффект на UE4. Один из этих методов был выбран мною.

Вы можете открыть изображения в новой вкладке, чтобы посмотреть их в более высоком разрешении.

Основные компоненты

Основная идея этого эффекта состоит в том, чтобы создать версию рендера сцены с помощью оператора Собеля, а затем смешать его с обычным рендером сцены на основе SphereMask, которую мы будем анимировать для создания эффекта сканирования.

Этот эффект состоит из 3 основных компонентов:

  • Масштабируемое поле SphereMask
  • Функция Sobel-Edge (я не буду объяснять, как работает эта функция, поскольку она является отдельной темой, но я сошлюсь на код, который использовал)
  • Наложение спроецированной текстуры на сетку мира

Масштабируемое поле SphereMask

Эта часть про то, как мы создаем масштабируемую SphereMask. Для этого передаем положение blueprint’а в набор параметров материала, после чего используем его следующим образом

Соедините результат ноды Clamp к emissive выходу вашего материала, и вы увидите что-то вроде этого

«TexLoc» — это vector3, который определяет местоположение источника сферы, в моем случае он читается из набора параметров материала, так что его можно считывать и из самой игры, например, для определения положения игрока.

Я использовал его только для показа результате в окне превью. Набор параметров нод, указанный выше, создает поле с радиусом сферы в 1024 юнита. Если вы хотите более детально изучить работу с дистанционными функциями и понять методы их использования, настоятельно рекомендую проверить сайт Inigo Quilez'a.

Теперь мы будем использовать Time для изменения масштаба сферы с установленным промежутком времени.

Это нам даст следующий результат

Мы умножаем время на 0. Frac(time) в основном дает нам постоянный период, который продолжает идти 0-1,0-1,0-1. 25, чтобы контролировать скорость масштабирования, а затем умножаем результат на радиус сферы, что приводит к изменению радиуса от 0 до 1024, и дает нам анимированную маску.

Нам нужно масштабирующее кольцо. Это хороший результат, но это не то, что мы хотим от эффекта. Это может быть легко сделано с помощью несложных вычислений.

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

5, и определяет край маски от текущей позиции с заданной шириной, что позволяет нам получить кольцо. Математические операции в блоке «Edge_Mask» в основном выбирают положение в маске градиента, в данном случае значение 0. Я не буду вдаваться в технические подробности получение краев у маски, скорее всего я расскажу об этом в одном из следующих постов.

Как вы видите, у вас полный контроль над шириной кольца без скалярного параметра, и если бы мы захотели, мы могли бы даже управлять затуханием краев, но в данном эффекте нам это не требуется.

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

Вы можете почитать о ней здесь, или вы можете использовать текстуру шума, которая содержит в себе World Aligned UV координаты. Для этого мы будем использовать ноду Vector Noise, который входит в состав UE4.

В моем шейдере я установил в ноде Vector Noise параметр Function в Cellnoise, не стесняйтесь экспериментировать с другими типами данного параметра, чтобы получить свой собственный уникальный эффект.

Результат будет выглядеть следующим образом

На этом первый этап нашего шейдера завершен, далее мы рассмотрим реализацию функции Sobel-Edge.

Функция Sobel-Edge

Существует много различных вариантов этой функции, некоторые из которых более оптимизированы, чем другие, я не буду объяснять ее суть, поскольку это является отдельной темой, но обычный поиск в Google по ключевым словам «Sobel Edge» или «Оператор Собеля» выдаст вам множество вариантов. Или воспользуйтесь статьей на хабре от Gepard_vvk — Алгоритмы выделения контуров изображений.

Далее мы сравниваем разницу в яркости, и если разница выше определенного порога, мы помечаем его как край, и в этом процессе мы получаем черно-белую маску текстуры RenderTarget, в которой на края налаживается маска. Основная идея оператора Собеля заключается в следующем: мы берем RenderTarget сцены (представьте, что это текстура, которая содержит то, что вы в данный момент видите в вашем окне просмотра) и сравниваете каждый пиксель со всеми соседними пикселями вокруг него.

Приведенный ниже код является простым примером функции оператора Собеля, которую создал RebelMoogle на сайте Shadertoy (скорее всего этот вариант не полностью оптимизирован, так что можете попробовать другую реализацию), мы воссоздадим ее в UE4 в нашем материале.

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{ vec2 uv = fragCoord.xy / iResolution.xy; vec3 TL = texture(iChannel0, uv + vec2(-1, 1)/ iResolution.xy).rgb; vec3 TM = texture(iChannel0, uv + vec2(0, 1)/ iResolution.xy).rgb; vec3 TR = texture(iChannel0, uv + vec2(1, 1)/ iResolution.xy).rgb; vec3 ML = texture(iChannel0, uv + vec2(-1, 0)/ iResolution.xy).rgb; vec3 MR = texture(iChannel0, uv + vec2(1, 0)/ iResolution.xy).rgb; vec3 BL = texture(iChannel0, uv + vec2(-1, -1)/ iResolution.xy).rgb; vec3 BM = texture(iChannel0, uv + vec2(0, -1)/ iResolution.xy).rgb; vec3 BR = texture(iChannel0, uv + vec2(1, -1)/ iResolution.xy).rgb; vec3 GradX = -TL + TR - 2.0 * ML + 2.0 * MR - BL + BR; vec3 GradY = TL + 2.0 * TM + TR - BL - 2.0 * BM - BR; fragColor.r = length(vec2(GradX.r, GradY.r)); fragColor.g = length(vec2(GradX.g, GradY.g)); fragColor.b = length(vec2(GradX.b, GradY.b));
}

В UE4 это выглядит следующим образом

Небольшое замечание о реализации функции — убедитесь, что ваши ноды SceneTexture настроены на использование PostProcessInput0

Две Custom ноды GradX и GradY, настройте их подобным образом

GradX:

return -TL + TR - 2.0 * ML + 2.0 * MR - BL + BR;

GradY:

return TL + 2.0 * TM + TR - BL - 2.0 * BM - BR;

Это не обязательно должно быть сделано в Custom, я использовал ее просто для удобства, так как в противном случае бы слишком много нод и образуется спагетти.

Если вы подключите результат функции в emissive выход материала, вы увидите следующее

Также мы умножаем результат на обычный vector3, чтобы сделать края любого цвета, который мы захотим.

В итоге изменяется цвет края

Наложение текстуры на сетку мира

Самая простая часть: мы просто используем текстуру сетки и проецируем ее по всему миру, а затем комбинируем ее с функцией Sobel-Edge, для получения крутого эффекта.

Если вы подключите результат функции в emissive выход, то увидите

Собираем все вместе

Теперь мы соберем все три части вместе для нашего пост эффекта!

Сначала мы объединим функцию Sobel-Edge и World-Aligned-Grid, сложив их вместе

Затем мы создаем ноду SceneTexture и добавляем в него результат из Sobel-Edge и World-Aligned-Grid.

Затем мы интерполируем между обычной сценой и добавленной, используя результат маски кольца, которую мы создали в первой части

Финальный результат будет выглядеть примерно так. И вуаля, мы это сделали. Вы можете, конечно, настроить параметры и попробовать изменить их значения, чтобы получить более интересные варианты.

Надеюсь вам пригодится данная информация, всего доброго 🙂

Пример проекта с данным шейдером можете найти на github.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть