Shaders applied to the screen

You may want to apply a shader to the whole screen to blur, stylize or even color correct.

Certain post-processing shaders, like glowing or depth of field, exist inside the WorldEnvironment node. However, you’ll have to create some effects yourself if you want to turn the screen into a pointilism drawing, or invert the colors for example.

Pointilism shader Inverted colors shader

There are two methods of applying shaders to the whole screen. One works with color data, while the other works with 3D depth. Well explore using color data here. We’ll look into 3D depth in 3D post processing shaders.

Viewports

First, we have to talk about parallel universes.

Grabbing the screen’s image data happens through a Viewport node. A viewport is a virtual screen onto which images are drawn. This view is separate from the main view and is not seen by the user. But we can display a viewport and then apply a shader to the node that displays it.

ViewportContainer

The simplest method is the ViewportContainer node. Its job is to resize and show viewports.

  1. Create a new User Interface scene.
  2. Add a ViewportContainer node.
  3. Add a Viewport node as a child of the container.
  4. Instance a scene or add some objects as children of the Viewport.

For 3D scenes, you need to include a camera in every viewport. We will talk more about this in the 3D masking lesson.

Save your scene, then go to Scene -> Reload Saved Scene to force the viewport container to resize, and you should see your scene.

To apply a shader, select the ViewportContainer, add a new ShaderMaterial to its Material property, and assign a new shader. ViewportContainers are canvas items and, just like sprites, can take shaders too.

ViewportTexture

The other method is the TextureRect that uses a ViewportTexture. ViewportContainers are easier to set up and less error prone, but TextureRects can be resized, stretched and distorted in ways containers cannot without separate shaders.

Note: We still use a ViewportContainer, but only for its ability to resize. It’s optional since you could set the resolution manually in the Size property.

  1. Create a new User Interface scene.
  2. Add a ViewportContainer node.
    1. Enable its Stretch property, set its Right and Bottom anchor to 1 so it takes up the whole screen.
    2. Set the ViewportContainer’s Self Modulate property’s alpha to 0 to hide it. (We can’t just set its visibility to 0, as we need Godot to update and render the contents.)
  3. Add a Viewport node as a child of the container.
  4. Instance a scene or add some objects as children of the Viewport.
  5. Add a TextureRect as a child of the root of the scene.
  6. In the Texture property, create a new ViewportTexture and select the Viewport.

Save your scene, then go to Scene -> Reload Saved Scene to force the viewport container and texture rectangle to resize, and you should see your scene.

Hint: because of the way Godot renders in the background, your image may be upside down. You can use the Flip V property to fix this.

If you want to change the size and position of the image - like making a small minimap in the corner - then you can control it like you would a regular TextureRect by enabling the Expand property and setting its Stretch Mode to Scale or Keep Aspect.

You can apply a new ShaderMaterial and a new shader to the TextureRect like you would with any canvas item.

Multiple passes

You may have wondered about it already, but what if the scene that’s under a Viewport is another Viewport? Or a Viewport of a Viewport of a Viewport of a Scene?

That is, in fact, how you would combine shaders into one final result! As long as they’re all sized, you can have any number of them. But it’s a good idea to split them up if you have a lot of them to avoid messy, unreadable scene trees.

Regardless of which way you make your viewports, learning to juggle them is an important skill to master and we’ll revisit this topic in future tutorials that involve post-processing.