自定义后期处理

简介

Godot提供许多开箱即用的后期处理效果, 包括Bloom, DOF和SSAO. 有时你想编写自己的自定义效果. 下面介绍如何做到这一点.

后处理效果是在Godot渲染后应用于帧的着色器. 首先想要将场景渲染为 Viewport, 然后在 ViewportTexture 中渲染 Viewport 并在屏幕上显示它.

实现自定义后处理着色器的最简单方法是使用Godot的内置功能从屏幕纹理中读取. 如果您不熟悉这个, 您应该先阅读 屏幕阅读着色器教程 .

注解

在撰写本文时,Godot不支持同时对多个缓冲区进行渲染. 处理之后着色器将无法访问法线或其他渲染通道. 你只能访问已渲染的帧.

单通后处理

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

注解

使用 Viewport 进行渲染可以控制场景的渲染方式, 包括帧率, 你可以使用 ViewportContainer 来渲染2D场景中的3D对象.

在这个演示中, 我们将使用一个 Node2D 与一个 ViewportContainer 和一个 Viewport . 你的 Scene 选项卡应该看起来像这样:

../../_images/post_hierarchy1.png

Viewport 内, 你可以有任何你想要的东西. 这将包含你的主场景. 在本教程中, 我们将使用一个随机盒子的领域:

../../_images/post_boxes.png

添加一个新的 ShaderMaterialViewportContainer , 并给它分配一个新的着色器资源. 你可以通过内置的 TEXTURE 制式来访问你渲染好的 Viewport .

注解

你可以选择不使用 ViewportContainer , 但如果这样做, 将需要在着色器中创建你自己的uniform, 并手动传递 Viewport 纹理, 像这样:

// Inside the Shader.
uniform sampler2D ViewportTexture;

您可以将纹理从GDScript传递到着色器中, 如下所示:

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

将以下代码复制到着色器. 上面的代码是单通道边缘检测滤波器, Sobel 滤波器 .

shader_type canvas_item;

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

注解

Sobel滤波器读取当前像素周围9x9网格中的像素, 并使用权重将它们加在一起. 令人感兴趣的是它为每个像素分配权重; 围绕中心的八个中的每一个都是+1, 对于中心像素是-8. 权重的选择称为 "核心". 您可以使用不同的内核来创建边缘检测过滤器, 轮廓和各种效果.

../../_images/post_outline.png

多通后处理

像模糊这样的后处理效果是资源密集型的. 但是如果您在多次通过中将它们分解, 您可以让它们运行得更快. 在多通道材质中, 每次传递都将前一次传递的结果作为输入并对其进行处理.

为了制作一个多通道的后期处理着色器, 你可以将 Viewport 节点堆叠起来. 在上面的例子中, 通过一个 ViewportContainer 节点, 将一个 Viewport 对象的内容渲染到根 Viewport . 通过将一个 Viewport 的内容渲染到另一个 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, UV).xyz * 0.16;
    col += texture(TEXTURE, UV + vec2(SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15;
    col += texture(TEXTURE, UV + vec2(-SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15;
    col += texture(TEXTURE, UV + vec2(2.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12;
    col += texture(TEXTURE, UV + vec2(2.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12;
    col += texture(TEXTURE, UV + vec2(3.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09;
    col += texture(TEXTURE, UV + vec2(3.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09;
    col += texture(TEXTURE, UV + vec2(4.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05;
    col += texture(TEXTURE, 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, UV).xyz * 0.16;
    col += texture(TEXTURE, UV + vec2(0.0, SCREEN_PIXEL_SIZE.y)).xyz * 0.15;
    col += texture(TEXTURE, UV + vec2(0.0, -SCREEN_PIXEL_SIZE.y)).xyz * 0.15;
    col += texture(TEXTURE, UV + vec2(0.0, 2.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.12;
    col += texture(TEXTURE, UV + vec2(0.0, 2.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.12;
    col += texture(TEXTURE, UV + vec2(0.0, 3.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.09;
    col += texture(TEXTURE, UV + vec2(0.0, 3.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.09;
    col += texture(TEXTURE, UV + vec2(0.0, 4.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.05;
    col += texture(TEXTURE, UV + vec2(0.0, 4.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.05;
    COLOR.xyz = col;
}

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

../../_images/post_blur.png

有关 Viewport 节点如何工作的更多信息, 请参阅 Viewports Tutorial .