Пользовательская пост-обработка
Введение
Godot предоставляет множество встроенных эффектов постобработки, включая Bloom, DOF и SSAO, которые описаны в документе Окружающая среда и постобработка. Однако для более сложных случаев могут потребоваться пользовательские эффекты. В этой статье объясняется, как создавать собственные пользовательские эффекты.
Самый простой способ реализовать собственный шейдер постобработки — использовать встроенную в Godot возможность чтения текстуры с экрана. Если вы не знакомы с этим, рекомендуем сначала прочитать Учебник по шейдерам для чтения с экрана.
Однопроходная постобработка
Эффекты постобработки — это шейдеры, применяемые к кадру после его рендеринга Godot. Чтобы применить шейдер к кадру, создайте CanvasLayer и присвойте ему ColorRect. Назначьте новый ShaderMaterial только что созданному ColorRect и установите для ColorRect якорный шаблон Full Rect:
Установка предустановки привязки на Full Rect на узле ColorRect
Ваше дерево сцен будет выглядеть примерно так:
Примечание
Другой более эффективный метод — использовать BackBufferCopy для копирования области экрана в буфер и доступа к ней в скрипте шейдера через sampler2D с использованием hint_screen_texture.
Примечание
На момент написания статьи Godot не поддерживает рендеринг в несколько буферов одновременно. Ваш шейдер постобработки не будет иметь доступа к другим проходам рендеринга и буферам, не предоставляемым Godot (например, к глубине или нормалям/шероховатости). Вам будет доступен только отрендеренный кадр и буферы, предоставляемые Godot в качестве сэмплеров.
Для этой демонстрации мы будем использовать Sprite овцы.
Назначьте новый Shader для ShaderMaterial объекта ColorRect. Доступ к текстуре и UV-координатам рамки можно получить с помощью sampler2D, используя hint_screen_texture и встроенные юниформы SCREEN_UV.
Скопируйте следующий код в свой шейдер. Этот код — шейдер шестнадцатеричной пикселизации от arlez80,
shader_type canvas_item;
uniform vec2 size = vec2(32.0, 28.0);
// If you intend to read from mipmaps with `textureLod()` LOD values greater than `0.0`,
// use `filter_nearest_mipmap` instead. This shader doesn't require it.
uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;
void fragment() {
vec2 norm_size = size * SCREEN_PIXEL_SIZE;
bool less_than_half = mod(SCREEN_UV.y / 2.0, norm_size.y) / norm_size.y < 0.5;
vec2 uv = SCREEN_UV + vec2(norm_size.x * 0.5 * float(less_than_half), 0.0);
vec2 center_uv = floor(uv / norm_size) * norm_size;
vec2 norm_uv = mod(uv, norm_size) / norm_size;
center_uv += mix(vec2(0.0, 0.0),
mix(mix(vec2(norm_size.x, -norm_size.y),
vec2(0.0, -norm_size.y),
float(norm_uv.x < 0.5)),
mix(vec2(0.0, -norm_size.y),
vec2(-norm_size.x, -norm_size.y),
float(norm_uv.x < 0.5)),
float(less_than_half)),
float(norm_uv.y < 0.3333333) * float(norm_uv.y / 0.3333333 < (abs(norm_uv.x - 0.5) * 2.0)));
COLOR = textureLod(screen_texture, center_uv, 0.0);
}
Овца будет выглядеть примерно так:
Многопроходная пост-обработка
Некоторые эффекты постобработки, такие как размытие, требуют больших ресурсов. Вы можете значительно ускорить их выполнение, разбив на несколько проходов. В многопроходном материале каждый проход принимает результат предыдущего прохода в качестве входных данных и обрабатывает его.
Для создания многопроходного шейдера постобработки узлы CanvasLayer и ColorRect объединяются в один. В приведённом выше примере объект CanvasLayer используется для рендеринга шейдера с использованием кадра на слое ниже. За исключением структуры узлов, шаги те же, что и для однопроходного шейдера постобработки.
Ваше дерево сцен будет выглядеть примерно так:
Например, вы можете реализовать полноэкранный эффект гауссова размытия, добавив следующие фрагменты кода к каждому узлу ColorRect. Порядок применения шейдеров зависит от положения CanvasLayer в дереве сцены: чем выше, тем быстрее. Для этого шейдера размытия порядок не имеет значения.
shader_type canvas_item;
uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;
// Blurs the screen in the X-direction.
void fragment() {
vec3 col = texture(screen_texture, SCREEN_UV).xyz * 0.16;
col += texture(screen_texture, SCREEN_UV + vec2(SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15;
col += texture(screen_texture, SCREEN_UV + vec2(-SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15;
col += texture(screen_texture, SCREEN_UV + vec2(2.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12;
col += texture(screen_texture, SCREEN_UV + vec2(2.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12;
col += texture(screen_texture, SCREEN_UV + vec2(3.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09;
col += texture(screen_texture, SCREEN_UV + vec2(3.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09;
col += texture(screen_texture, SCREEN_UV + vec2(4.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05;
col += texture(screen_texture, SCREEN_UV + vec2(4.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05;
COLOR.xyz = col;
}
shader_type canvas_item;
uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;
// Blurs the screen in the Y-direction.
void fragment() {
vec3 col = texture(screen_texture, SCREEN_UV).xyz * 0.16;
col += texture(screen_texture, SCREEN_UV + vec2(0.0, SCREEN_PIXEL_SIZE.y)).xyz * 0.15;
col += texture(screen_texture, SCREEN_UV + vec2(0.0, -SCREEN_PIXEL_SIZE.y)).xyz * 0.15;
col += texture(screen_texture, SCREEN_UV + vec2(0.0, 2.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.12;
col += texture(screen_texture, SCREEN_UV + vec2(0.0, 2.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.12;
col += texture(screen_texture, SCREEN_UV + vec2(0.0, 3.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.09;
col += texture(screen_texture, SCREEN_UV + vec2(0.0, 3.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.09;
col += texture(screen_texture, SCREEN_UV + vec2(0.0, 4.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.05;
col += texture(screen_texture, SCREEN_UV + vec2(0.0, 4.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.05;
COLOR.xyz = col;
}
Используя приведенный выше код, вы должны получить эффект размытия на весь экран, как показано ниже.