Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

Pós-processamento personalizado

Introdução

O Godot oferece muitos efeitos de pós-processamento prontos para uso, incluindo Bloom, DOF e SSAO, que são descritos em Ambiente e pós-processamento. No entanto, casos de uso avançados podem exigir efeitos personalizados. Este artigo explica como escrever seus próprios efeitos personalizados.

A maneira mais fácil de implementar um shader de pós-processamento personalizado é usar a capacidade integrada do Godot de ler a textura da tela. Se você não está familiarizado com isso, deve ler o Screen Reading Shaders Tutorial primeiro.

Pós-processamento de passagem única

Efeitos de pós-processamento são shaders aplicados a um frame após o Godot tê-lo renderizado. Para aplicar um shader a um frame, crie um CanvasLayer e dê a ele um ColorRect. Atribua um novo ShaderMaterial ao recém-criado ColorRect e defina o preset de âncora do ColorRect para Full Rect:

Configurando o preset de âncora para Full Rect no nó ColorRect

Configurando o preset de âncora para Full Rect no nó ColorRect

Sua árvore de cena ficará parecida com isto:

../../_images/post_tree1.png

Nota

Outro método mais eficiente é usar um BackBufferCopy para copiar uma região da tela para um buffer e acessá-la em um script de shader através de um sampler2D usando hint_screen_texture.

Nota

Até o momento da redação deste artigo, o Godot não suporta a renderização em múltiplos buffers ao mesmo tempo. Seu shader de pós-processamento não terá acesso a outros passes de renderização e buffers não expostos pelo Godot (como depth ou normal/roughness). Você tem apenas acesso ao frame renderizado e aos buffers expostos pelo Godot como samplers.

Para esta demonstração, usaremos este Sprite de uma ovelha.

../../_images/post_example1.png

Atribua um novo Shader ao ShaderMaterial do ColorRect. Você pode acessar a textura e o UV do frame com um sampler2D usando hint_screen_texture e os uniforms integrados do SCREEN_UV.

Copie o seguinte código para o seu shader. O código abaixo é um shader de pixelização hexagonal por 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);
}

A ovelha ficará parecida com isto:

../../_images/post_example2.png

Pós-processamento multipassagem

Alguns efeitos de pós-processamento, como desfoques (blurs), consomem muitos recursos. Você pode fazê-los rodar muito mais rápido se os dividir em múltiplos passes. Em um material multipass, cada passe recebe o resultado do passe anterior como entrada e o processa.

Para produzir um shader de pós-processamento multipass, você empilha nós CanvasLayer e ColorRect. No exemplo acima, você usa um objeto CanvasLayer para renderizar um shader usando o frame da camada abaixo. Fora a estrutura dos nós, os passos são os mesmos do shader de pós-processamento de passe único.

Sua árvore de cena ficará parecida com isto:

../../_images/post_tree2.png

Como exemplo, você poderia escrever um efeito de desfoque gaussiano em tela cheia anexando os seguintes trechos de código a cada um dos nós ColorRect. A ordem em que você aplica os shaders depende da posição do CanvasLayer na árvore da cena: quanto mais alto, mais cedo. Para este shader de desfoque, a ordem não importa.

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;
}

Usando o código acima, você deve acabar com um efeito de desfoque em tela cheia como o abaixo.

../../_images/post_example3.png