Post procesado personalizado¶
Introducción¶
Godot proporciona muchos efectos de post-procesamiento fuera del paquete, incluyendo Bloom, DOF, y SSAO. A veces quieres escribir tu propio efecto personalizado. Así es como puedes hacerlo.
Los efectos de post-procesamiento son shaders aplicados a un fotograma después de que Godot lo haya renderizado. Primero quieres renderizar tu escena en un Viewport, luego renderizar el Viewport
dentro de un ViewportTexture y mostrarlo en la pantalla.
La forma más fácil de implementar un shader personalizado de post-procesamiento es usar la capacidad incorporada de Godot para leer la textura de la pantalla. Si no estás familiarizado con esto, deberías leer primero el Screen Reading Shaders Tutorial.
Nota
En el momento de escribir este artículo, Godot no soporta la renderización a múltiples buffers al mismo tiempo. Su shader de post-procesamiento no tendrá acceso a los normales u otros pases de renderización. Sólo tiene acceso al fotograma renderizado.
Post procesado de paso simple¶
Necesitarás un Viewport
para renderizar tu escena, y una escena para renderizar tu Viewport
en la pantalla. Puedes usar un ViewportContainer para mostrar tu Viewport
en toda la pantalla o dentro de otro nodo Control.
Nota
Renderizar usando un Viewport
te da control sobre cómo se renderiza la escena, incluyendo la velocidad de fotogramas, y puedes usar el ViewportContainer
para renderizar objetos 3D en una escena 2D.
Para esta demostración, usaremos un Node2D con un ViewportContainer
y finalmente un Viewport
. Tu pestaña Scene debería verse así:
Dentro del Viewport
, puedes tener lo que quieras. Esto contendrá tu escena principal. Para este tutorial, usaremos un campo de cajas aleatorias:
Añade un nuevo ShaderMaterial al ViewportContainer
y asígnale un nuevo recurso shader. Puedes acceder a tu Viewport
renderizado con el uniform incorporado TEXTURE
.
Nota
Puedes elegir no usar un ViewportContainer
, pero si lo haces, necesitarás crear tu propio uniforme en el shader y pasar la textura Viewport
manualmente, así:
// Inside the Shader.
uniform sampler2D ViewportTexture;
Y puedes pasar la textura al shader desde el GDScript así:
# In GDScript.
func _ready():
$Sprite.material.set_shader_param("ViewportTexture", $Viewport.get_texture())
Copia el siguiente código en tu shader. El código anterior es un filtro de detección de bordes de un solo paso, un filtro 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;
}
Nota
El filtro Sobel lee los píxeles en una cuadrícula de 9x9 alrededor del píxel actual y los suma, usando el peso. Lo que lo hace interesante es que asigna pesos a cada píxel; +1 para cada uno de los ocho alrededor del centro y -8 para el píxel del centro. La elección de los pesos se llama "kernel". Puedes usar diferentes kernels para crear filtros de detección de bordes, contornos y todo tipo de efectos.
Post procesado de pasos múltiples¶
Algunos efectos de post-procesamiento como el difuminado son intensivos en recursos. Sin embargo, si los descompones en múltiples pasadas, puedes hacer que se ejecuten mucho más rápido. En un material de múltiples pasadas, cada pasada toma el resultado de la pasada anterior como entrada y lo procesa.
Para hacer un shader de post-procesamiento multi-paso, apilas los nodos Viewport
. En el ejemplo de arriba, has llevado el contenido de un objeto Viewport
a la raíz Viewport
, a través de un nodo ViewportContainer
. Puedes hacer lo mismo para un shader multi-pass renderizando el contenido de un Viewport
en otro y luego renderizando el último Viewport
en el Viewport
raíz.
La escena final debería verse similar a esta:
Godot hará primero el nodo inferior Viewport
. Así que si el orden de los pases es importante para tus shaders, asegúrate de asignar el shader que quieres aplicar primero al ViewportContainer
más bajo del árbol.
Nota
También puedes renderizar tus Viewports por separado sin anidarlos así. Solo necesitas usar dos Viewports y renderizarlos uno tras otro.
Aparte de la estructura de nodos, los pasos son los mismos que en el shader de post-procesamiento de una sola pasada.
Como ejemplo, podrías escribir un efecto de desenfoque gaussiano a pantalla completa adjuntando las siguientes piezas de código a cada uno de los ViewportContainers. El orden en que se aplican los shaders no importa:
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;
}
Usando el código anterior, deberías terminar con un efecto de desenfoque en pantalla completa como el de abajo.
Para más información sobre cómo funcionan los nodos Viewport
, lee Viewports Tutorial.