Індивідуальна постобробка

Вступ

Godot надає багато ефектів постобробки з коробки, включаючи Bloom, DOF і SSAO, які описані в Середовище та пост-обробка. Однак для розширених випадків використання можуть знадобитися спеціальні ефекти. У цій статті пояснюється, як написати власні ефекти.

Найпростіший спосіб реалізувати власний шейдер постобробки - використати вбудовану у Godot можливість зчитування текстури з екрана. Якщо ви не знайомі з цією можливістю, вам слід спершу прочитати Screen Reading Shaders Tutorial.

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

Post-processing effects are shaders applied to a frame after Godot has rendered it. To apply a shader to a frame, create a CanvasLayer, and give it a ColorRect. Assign a new ShaderMaterial to the newly created ColorRect, and set the ColorRect's anchor preset to Full Rect:

Setting the anchor preset to Full Rect on the ColorRect node

Setting the anchor preset to Full Rect on the ColorRect node

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

../../_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