Up to date
This page is up to date for Godot 4.0
.
If you still find outdated information, please open an issue.
Advanced post-processing¶
Introduction¶
This tutorial describes an advanced method for post-processing in Godot. In particular, it will explain how to write a post-processing shader that uses the depth buffer. You should already be familiar with post-processing generally and, in particular, with the methods outlined in the custom post-processing tutorial.
In the previous post-processing tutorial, we rendered the scene to a Viewport and then rendered the Viewport in a SubViewportContainer to the main scene. One limitation of this method is that we could not access the depth buffer because the depth buffer is only available in shaders and Viewports do not maintain depth information.
Full screen quad¶
In the custom post-processing tutorial, we covered how to use a Viewport to make custom post-processing effects. There are two main drawbacks of using a Viewport:
The depth buffer cannot be accessed
The effect of the post-processing shader is not visible in the editor
To get around the limitation on using the depth buffer, use a MeshInstance3D with a QuadMesh primitive. This allows us to use a shader and to access the depth texture of the scene. Next, use a vertex shader to make the quad cover the screen at all times so that the post-processing effect will be applied at all times, including in the editor.
First, create a new MeshInstance3D and set its mesh to a QuadMesh. This creates
a quad centered at position (0, 0, 0)
with a width and height of 1
. Set
the width and height to 2
and enable Flip Faces. Right now, the quad
occupies a position in world space at the origin. However, we want it to move
with the camera so that it always covers the entire screen. To do this, we will
bypass the coordinate transforms that translate the vertex positions through the
difference coordinate spaces and treat the vertices as if they were already in
clip space.
The vertex shader expects coordinates to be output in clip space, which are coordinates
ranging from -1
at the left and bottom of the screen to 1
at the top and right
of the screen. This is why the QuadMesh needs to have height and width of 2
.
Godot handles the transform from model to view space to clip space behind the scenes,
so we need to nullify the effects of Godot's transformations. We do this by setting the
POSITION
built-in to our desired position. POSITION
bypasses the built-in transformations
and sets the vertex position directly.
shader_type spatial;
void vertex() {
POSITION = vec4(VERTEX, 1.0);
}
Even with this vertex shader, the quad keeps disappearing. This is due to frustum culling, which is done on the CPU. Frustum culling uses the camera matrix and the AABBs of Meshes to determine if the Mesh will be visible before passing it to the GPU. The CPU has no knowledge of what we are doing with the vertices, so it assumes the coordinates specified refer to world positions, not clip space positions, which results in Godot culling the quad when we turn away from the center of the scene. In order to keep the quad from being culled, there are a few options:
Add the QuadMesh as a child to the camera, so the camera is always pointed at it
Set the Geometry property
extra_cull_margin
as large as possible in the QuadMesh
The second option ensures that the quad is visible in the editor, while the first option guarantees that it will still be visible even if the camera moves outside the cull margin. You can also use both options.
Depth texture¶
To read from the depth texture, we first need to create a texture uniform set to the depth buffer
by using hint_depth_texture
.
uniform sampler2D depth_texture : source_color, hint_depth_t