- PVSM.RU - https://www.pvsm.ru -

В эти выходные у меня появилось немного свободного времени между занятиями (прим. автор на момент статьи получал степень магистра наук), и я решил вернуться к созданию шейдеров, придумав этот postprocess эффект сканирования. Я представил, что он используется в игре как своего рода эффект сканирования на расстоянии. Также мы используем некоторые простые искажения шума, чтобы эффект выглядел немного интереснее.
В этой статье я расскажу, как реализовать данный эффект на UE4. Существуют несколько способов, с помощью которых вы можете создать этот эффект. Один из этих методов был выбран мною.
Вы можете открыть изображения в новой вкладке, чтобы посмотреть их в более высоком разрешении.
Основная идея этого эффекта состоит в том, чтобы создать версию рендера сцены с помощью оператора Собеля [1], а затем смешать его с обычным рендером сцены на основе SphereMask, которую мы будем анимировать для создания эффекта сканирования.
Этот эффект состоит из 3 основных компонентов:
Эта часть про то, как мы создаем масштабируемую SphereMask. Для этого передаем положение blueprint’а в набор параметров материала, после чего используем его следующим образом

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

«TexLoc» — это vector3, который определяет местоположение источника сферы, в моем случае он читается из набора параметров материала, так что его можно считывать и из самой игры, например, для определения положения игрока.
Набор параметров нод, указанный выше, создает поле с радиусом сферы в 1024 юнита. Я использовал его только для показа результате в окне превью. Если вы хотите более детально изучить работу с дистанционными функциями и понять методы их использования, настоятельно рекомендую проверить сайт Inigo Quilez'a [2].
Теперь мы будем использовать Time для изменения масштаба сферы с установленным промежутком времени.

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

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

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

Математические операции в блоке «Edge_Mask» в основном выбирают положение в маске градиента, в данном случае значение 0.5, и определяет край маски от текущей позиции с заданной шириной, что позволяет нам получить кольцо. Я не буду вдаваться в технические подробности получение краев у маски, скорее всего я расскажу об этом в одном из следующих постов.
Как вы видите, у вас полный контроль над шириной кольца без скалярного параметра, и если бы мы захотели, мы могли бы даже управлять затуханием краев, но в данном эффекте нам это не требуется.
Следующим шагом будет использование шума, чтобы создать визуально интересный вариант кольца.
Для этого мы будем использовать ноду Vector Noise, который входит в состав UE4. Вы можете почитать о ней здесь [3], или вы можете использовать текстуру шума, которая содержит в себе World Aligned UV координаты.
В моем шейдере я установил в ноде Vector Noise параметр Function в Cellnoise, не стесняйтесь экспериментировать с другими типами данного параметра, чтобы получить свой собственный уникальный эффект.

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

На этом первый этап нашего шейдера завершен, далее мы рассмотрим реализацию функции Sobel-Edge.
Существует много различных вариантов этой функции, некоторые из которых более оптимизированы, чем другие, я не буду объяснять ее суть, поскольку это является отдельной темой, но обычный поиск в Google по ключевым словам «Sobel Edge» или «Оператор Собеля» выдаст вам множество вариантов. Или воспользуйтесь статьей на хабре от Gepard_vvk [4] — Алгоритмы выделения контуров изображений [5].
Основная идея оператора Собеля заключается в следующем: мы берем RenderTarget сцены (представьте, что это текстура, которая содержит то, что вы в данный момент видите в вашем окне просмотра) и сравниваете каждый пиксель со всеми соседними пикселями вокруг него. Далее мы сравниваем разницу в яркости, и если разница выше определенного порога, мы помечаем его как край, и в этом процессе мы получаем черно-белую маску текстуры RenderTarget, в которой на края налаживается маска.
Приведенный ниже код является простым примером функции оператора Собеля, которую создал RebelMoogle [6] на сайте 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 [7].
Автор: Александр Тарабака
Источник [8]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/razrabotka-igr/304388
Ссылки в тексте:
[1] оператора Собеля: https://ru.wikipedia.org/wiki/%D0%9E%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D0%BE%D1%80_%D0%A1%D0%BE%D0%B1%D0%B5%D0%BB%D1%8F
[2] сайт Inigo Quilez'a: http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm
[3] здесь: https://docs.unrealengine.com/latest/INT/Engine/Rendering/Materials/ExpressionReference/Vector/#vectornoise
[4] Gepard_vvk: https://habr.com/users/gepard_vvk/
[5] Алгоритмы выделения контуров изображений: https://habr.com/post/114452/
[6] RebelMoogle: https://www.shadertoy.com/view/4t3XDM
[7] github: https://github.com/UnrealPlayground/PostProcessScanEffect
[8] Источник: https://habr.com/post/435240/?utm_source=habrahabr&utm_medium=rss&utm_campaign=435240
Нажмите здесь для печати.