自定义后处理

介绍

Godot提供许多现成的后处理效果,包括Bloom、DOF和SSAO。有时你想写自己的自定义效果。你可以这样做。

后处理效果是在Godot渲染后应用于帧的明暗器。首先要将场景渲染为 Viewport ,然后渲染 Viewport 里面 ViewportTexture 并在屏幕上显示。

实现自定义后处理明暗器的最简单方法是使用Godot的内置功能读取屏幕纹理。如果你不熟悉这个,你应该阅读 Screen Reading Shaders Tutorial 第一。

注解

在编写时,godot不支持同时渲染到多个缓冲区。后处理材质球将无法访问法线或其他渲染过程。您只能访问渲染帧。

单道后处理

你需要一个 Viewport 将场景渲染到,并将场景渲染到 Viewport 在屏幕上。您可以使用 ViewportContainer 显示您的 Viewport 在整个屏幕上或在另一个屏幕内 Control 节点。

注解

使用 Viewport 使您能够控制场景渲染的方式,包括帧速率,并且可以使用 ViewportContainer 在二维场景中渲染三维对象。

对于这个演示,我们将使用 Node2D 用一个 ViewportContainer 最后是一个 Viewport . 你的 场景 选项卡应如下所示:

../../_images/post_hierarchy1.png

里面 Viewport 你想要什么都可以。这将包含您的主场景。对于本教程,我们将使用随机框字段:

../../_images/post_boxes.png

添加新的 ShaderMaterialViewportContainer ,并为其分配新的明暗器资源。您可以访问渲染的 Viewport 内置 TEXTURE 制服。

注解

您可以选择不使用 ViewportContainer ,但如果这样做,则需要在材质球中创建自己的统一体并通过 Viewport 手动纹理,如:

// Inside the Shader
uniform sampler2D ViewportTexture;

您可以通过gdscript将纹理传递到着色器中,如下所示:

# In GDScript
func _ready():
  $Sprite.material.set_shader_param("ViewportTexture", $Viewport.get_texture())

将以下代码复制到您的明暗器。上述代码是一个单通边缘检测滤波器, Sobel filter .

shader_type canvas_item;

void fragment() {
    vec3 col = -8.0 * texture(TEXTURE, SCREEN_UV).xyz;
    col += texture(TEXTURE, SCREEN_UV + vec2(0.0, SCREEN_PIXEL_SIZE.y)).xyz;
    col += texture(TEXTURE, SCREEN_UV + vec2(0.0, -SCREEN_PIXEL_SIZE.y)).xyz;
    col += texture(TEXTURE, SCREEN_UV + vec2(SCREEN_PIXEL_SIZE.x, 0.0)).xyz;
    col += texture(TEXTURE, SCREEN_UV + vec2(-SCREEN_PIXEL_SIZE.x, 0.0)).xyz;
    col += texture(TEXTURE, SCREEN_UV + SCREEN_PIXEL_SIZE.xy).xyz;
    col += texture(TEXTURE, SCREEN_UV - SCREEN_PIXEL_SIZE.xy).xyz;
    col += texture(TEXTURE, SCREEN_UV + vec2(-SCREEN_PIXEL_SIZE.x, SCREEN_PIXEL_SIZE.y)).xyz;
    col += texture(TEXTURE, SCREEN_UV + vec2(SCREEN_PIXEL_SIZE.x, -SCREEN_PIXEL_SIZE.y)).xyz;
    COLOR.xyz = col;
}

注解

Sobel过滤器读取当前像素周围9x9网格中的像素,并使用权重将它们相加。有趣的是,它为每个像素指定了权重;+1代表围绕中心的八个像素,-8代表中心像素。权重的选择被称为“内核”。您可以使用不同的内核来创建边缘检测过滤器、轮廓和各种效果。

../../_images/post_outline.png

多道次后处理

一些后期处理效果(如模糊)是资源密集型的。但是,如果你在多个传球中把他们分开,你可以让他们跑得更快。在多径材料中,每一道都将前一道的结果作为输入并进行处理。

要制作多通道后处理明暗器,请堆叠 Viewport 节点。在上面的示例中,您呈现了 Viewport 对象到根目录中 Viewport ,通过A ViewportContainer 节点。您可以通过渲染一个材质球的内容来对多过程材质球执行相同的操作。 Viewport 然后渲染最后一个 Viewport 在根部 Viewport .

场景层次结构如下所示:

../../_images/post_hierarchy2.png

Godot将粉底 Viewport 首先是节点。因此,如果传递顺序对您的明暗器很重要,请确保将要应用的明暗器首先指定给最低的明暗器 ViewportContainer 在树上。

注解

也可以单独渲染视口,而无需像这样嵌套它们。您只需要使用两个视区并逐个渲染它们。

除了节点结构,步骤与单通道后处理明暗器相同。

作为一个例子,您可以通过将以下代码附加到每个 ViewportContainers . 应用材质球的顺序并不重要:

shader_type canvas_item;

//Blurs the screen in the X-direction.
void fragment() {
    vec3 col = texture(TEXTURE, SCREEN_UV).xyz * 0.16;
    col += texture(TEXTURE, SCREEN_UV + vec2(SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15;
    col += texture(TEXTURE, SCREEN_UV + vec2(-SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15;
    col += texture(TEXTURE, SCREEN_UV + vec2(2.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12;
    col += texture(TEXTURE, SCREEN_UV + vec2(2.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12;
    col += texture(TEXTURE, SCREEN_UV + vec2(3.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09;
    col += texture(TEXTURE, SCREEN_UV + vec2(3.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09;
    col += texture(TEXTURE, SCREEN_UV + vec2(4.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05;
    col += texture(TEXTURE, SCREEN_UV + vec2(4.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05;
    COLOR.xyz = col;
}
shader_type canvas_item;

//Blurs the screen in the Y-direction.
void fragment() {
    vec3 col = texture(TEXTURE, SCREEN_UV).xyz * 0.16;
    col += texture(TEXTURE, SCREEN_UV + vec2(0.0, SCREEN_PIXEL_SIZE.y)).xyz * 0.15;
    col += texture(TEXTURE, SCREEN_UV + vec2(0.0, -SCREEN_PIXEL_SIZE.y)).xyz * 0.15;
    col += texture(TEXTURE, SCREEN_UV + vec2(0.0, 2.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.12;
    col += texture(TEXTURE, SCREEN_UV + vec2(0.0, 2.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.12;
    col += texture(TEXTURE, SCREEN_UV + vec2(0.0, 3.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.09;
    col += texture(TEXTURE, SCREEN_UV + vec2(0.0, 3.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.09;
    col += texture(TEXTURE, SCREEN_UV + vec2(0.0, 4.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.05;
    col += texture(TEXTURE, SCREEN_UV + vec2(0.0, 4.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.05;
    COLOR.xyz = col;
}

使用上面的代码,您应该得到一个全屏模糊效果,如下所示。

../../_images/post_blur.png

有关如何 Viewport 节点工作,请参见 Viewports Tutorial .