Пользовательская пост-обработка

Введение

Godot предоставляет множество встроенных эффектов постобработки, включая Bloom, DOF и SSAO, которые описаны в документе Окружающая среда и постобработка. Однако для более сложных случаев могут потребоваться пользовательские эффекты. В этой статье объясняется, как создавать собственные пользовательские эффекты.

Самый простой способ реализовать собственный шейдер постобработки — использовать встроенную в Godot возможность чтения текстуры с экрана. Если вы не знакомы с этим, рекомендуем сначала прочитать Учебник по шейдерам для чтения с экрана.

Однопроходная постобработка

Эффекты постобработки — это шейдеры, применяемые к кадру после его рендеринга Godot. Чтобы применить шейдер к кадру, создайте CanvasLayer и присвойте ему ColorRect. Назначьте новый ShaderMaterial только что созданному ColorRect и установите для ColorRect якорный шаблон Full Rect:

Установка предустановки привязки на Full Rect на узле ColorRect

Установка предустановки привязки на Full Rect на узле ColorRect

Ваше дерево сцен будет выглядеть примерно так:

../../_images/post_tree1.png

Примечание

Другой более эффективный метод — использовать BackBufferCopy для копирования области экрана в буфер и доступа к ней в скрипте шейдера через sampler2D с использованием hint_screen_texture.

Примечание

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

Для этой демонстрации мы будем использовать Sprite овцы.

../../_images/post_example1.png

Назначьте новый 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);
}

Овца будет выглядеть примерно так:

../../_images/post_example2.png

Многопроходная пост-обработка

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

Для создания многопроходного шейдера постобработки узлы CanvasLayer и ColorRect объединяются в один. В приведённом выше примере объект CanvasLayer используется для рендеринга шейдера с использованием кадра на слое ниже. За исключением структуры узлов, шаги те же, что и для однопроходного шейдера постобработки.

Ваше дерево сцен будет выглядеть примерно так:

../../_images/post_tree2.png

Например, вы можете реализовать полноэкранный эффект гауссова размытия, добавив следующие фрагменты кода к каждому узлу 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;
}

Используя приведенный выше код, вы должны получить эффект размытия на весь экран, как показано ниже.

../../_images/post_example3.png