Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

自訂後期處理

前言

Godot provides many post-processing effects out of the box, including Bloom, DOF, and SSAO, which are described in 環境和後期處理. However, advanced use cases may require custom effects. This article explains how to write your own custom effects.

實作自訂後期處理著色器的最簡單方法是使用Godot的內建功能從螢幕紋理中讀取. 如果您不熟悉這個, 您應該先閱讀 螢幕閱讀著色器教學 .

單通後期處理

後處理效果是在 Godot 渲染影格後應用於影格的著色器。若要將著色器套用到影格,請建立一個 CanvasLayer <class_CanvasLayer>`,並為其指定一個 ColorRect <class_ColorRect>`。將新的 ShaderMaterial <class_ShaderMaterial> 指派給新建立的“ColorRect”,並將“ColorRect”的佈局設為“完整矩形”。

最後場景應該長這樣。

../../_images/post_tree1.png

備註

另一種更有效的方法是使用 BackBufferCopy <class_BackBufferCopy>` 將螢幕區域複製到緩衝區,並使用「hint_screen_texture」透過「sampler2D」在著色器腳本中存取它。

備註

As of the time of writing, Godot does not support rendering to multiple buffers at the same time. Your post-processing shader will not have access to other render passes and buffers not exposed by Godot (such as depth or normal/roughness). You only have access to the rendered frame and buffers exposed by Godot as samplers.

這個演示中,我們使用的是這張小羊的:ref:精靈 <class_Sprite2D>

../../_images/post_example1.png

ColorRectShaderMaterial 分配一個新的 Shader。你可以通過使用帶 hint_screen_texturesampler2D 以及內建的 uniform SCREEN_UV 來存取這一影格的紋理和 UV。

將以下程式碼複製到著色器. 上面的程式碼是單通道邊緣偵測濾波器, Sobel 濾波器

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 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(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(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