Android&iOS

Introduction to Shaders in Godot 4


Shaders are powerful tools that allow developers to customize the look and feel of their games in endless ways. This article will go over the basics of writing fragment and vertex shaders in Godot 4. You’ll learn techniques to create your own visual effects. From manipulating texture colors to animating sprites, you’ll discover all the building blocks you need to start experimenting with shaders in your own projects.

Note: This article assumes you’re familiar with Godot’s editor. Basic understanding of programming and math are also recommended, but not required.

Getting Started

To follow along with this tutorial, download the project materials via the Download materials link at the top or bottom of the page. Inside the zip file, you’ll find a starter and a final folder, both containing a project folder named ShaderIntroduction.
To start with, open the starter project in Godot. You should see an empty main scene with a Node2D node named Main at its root. This is where’ll you’ll be adding sprites to experiment with shaders.

The starter project comes with a set of resources in the form of sprites. You can find these in the textures folder, along with the default Godot icon and a simple colorful image at the root of the Filesystem.

Resources

As you can see, there’s not much going on yet, but you’ll get to it soon!

What is a Shader?

Before writing any shaders, it’s important to understand what they are. A shader is a set of instructions that runs on your graphics card and defines the appearance of rendered objects like sprites and 3D objects. Modern rendering pipelines make heavy use of shaders to create effects like specular lighting, volumetric fog and post-processing effects.

Besides creating impressive visual effects, you can also use shaders to manipulate the look and feel of your game. You can make trees sway in the wind, or create a shader that makes a sprite blink when it gets hit for example. These little programs can add a lot of life to your projects.

Tree swaying

One of the features that make shaders special is their ability to run in parallel. Instead of a classic program that runs on the CPU and has to finish its tasks one after the other, a shader can do lots of tasks at once. This is crucial as shaders often manipulate every single pixel on your screen or every vertex of a complex 3D object many times per second. At 4K resolution, a single shader might be working on more than 8 million pixels at once!

When you write a shader, you’ll be working in a specialized language called a Shading Language. Some of the more popular languages include OpenGL Shading Language (GLSL) and High Level Shading Language (HLSL). To run the shader, your CPU translates the code into instructions that the GPU can understand, this is known as compiling.

The CPU performs the compilation process while a game is initializing and partially while running the game as shaders can be dynamically changed during gameplay. The compiled shaders are then cached on disk for future use. For modern titles with thousands of shaders, this process can take a while, which is why you’re often presented with a loading screen that says “Compiling shaders”.

Types of Shaders

As mentioned above, shaders are versatile. Godot supports the creation of the following types of shaders:

  • Spatial: Used for manipulating the rendering of 3D objects.
  • Canvas item: This changes the look and feel of 2D objects like sprites and UI elements.
  • Particles: These shaders manipulate the way a particle system behaves.
  • Sky: Used for rendering sky backgrounds and cubemaps.
  • Fog: These are specialized shaders used for volumetric fog effects.

The spatial and canvas item shaders are the most common types of shaders as just about every game out there uses 3D objects or sprites. The others are reserved for niche cases.
To build up a basic understanding, you’ll be creating canvas item shaders throughout this tutorial.

Basics of Texture Manipulation

Alright, enough theory for now! It’s time to write your first shader, a fragment shader. Fragment shaders can alter the color of a surface, be it a sprite or a 3D object.
Shaders are a type of resource in Godot, so their creation is the same as any other resource. Create a new folder in the shaders folder named fragment. Right-click the fragment folder and select Create New ▸ Resource….

Create new resource

Search for “shader” and double-click the first match that gets selected: Shader.

Search shader

You’ll now see a Create Shader dialog with some options. Change the Mode to Canvas Item.

Canvas Item Mode

Next, name this new shader UV_to_color.gdshader and click the Create button.

UV to color shader

Double-click the shader you created to open it in the shader editor.

Shader Editor

This editor is similar to Godot’s script editor, but more minimal. It supports auto-completion and syntax highlighting, but you won’t be able to debug shaders or search for help about functions.

Fragment Shaders

The code you’re seeing here is the bare minimum required to create a fragment shader. It’s written in Godot’s own shading language called GDShader, which is similar to GLSL but simplified. It’s a high-level language with a syntax based on the C programming language.

This shader consists of two parts: the shader_type and three functions: vertex, fragment and light. The shader_type tells Godot what kind of shader you’re working with. In this case, it’s a canvas item shader meant to change the color and/or texture of a canvas item, the class all sprites and UI elements derive from.

The functions are the heart of the shader, they’re called processor functions and are the entry points of your shader. For example, The GPU will call the fragment function for every pixel of the canvas item you attach it to, along with some information about that pixel.

To apply this shader to a sprite, drag icon.svg from the FileSystem onto the 2D viewport first. This will add a Sprite2D node with the icon as its texture to the scene. Name this node UV.

New sprite added

Select the UV node and expand its Material category in the Inspector. You should now see the Material property with a dropdown next to it.

Material dropdown

One way of applying the shader is by creating a new ShaderMaterial here and picking the shader file as its input, but I’ll share a much faster way! Simply drag UV_to_color.gdshader from the FileSystem onto the Material property. This will create the ShaderMaterial for you.

Drag shader file to material

Click on the new ShaderMaterial resource to show its properties and you’ll notice the shader is already set. Nice and easy.

Shader applied

Now take a look at the sprite again and you’ll see nothing has changed. This is because your shader isn’t doing anything yet. Time to change that with some code.
Edit the UV_to_color shader’s fragment function like below and press CTRL/CMD-S to save it:

void fragment() {
    COLOR = vec4(UV.x, UV.y, 0.0, 1.0);
}

Godot updates shaders right away in its editor, so there’s now a dramatic change to the sprite. It looks like a colorful rectangle of gradients.

Colorful rectangle

To explain why this happened, I’ll dissect the code you added:

  • Everything inside the curly brackets of the fragment() function runs on every pixel of the sprite. You can compare this to a GDScript for loop:
for pixel in canvas_item.pixels:
    fragment(pixel)
  • COLOR is a built-in variable that represents the color of the current pixel. It’s a vec4, a vector with 4 floating-point components: r, g, b and a, representing the red, green, blue and alpha components of the color. By changing the value of COLOR, you change the pixel color.
  • = vec4(UV.x, UV.y, 0.0, 1.0) is an expression that returns a vec4 with the values of UV.x, UV.y, 0.0 and 1.0 for the new pixel color in RGBA order. In this case, the blue component is absent by setting it to 0.0, while the alpha component is set to 1.0 for full opacity.
  • UV is a built-in variable that represents the normalized position of the current pixel. It’s a vec2, a vector with 2 floating-point components that range from 0.0 to 1.0. A pixel in the upper-left corner has a value of (X: 0, Y: 0) and a pixel in the lower-right corner has a value of (X: 1, Y: 1). The X value of UV gets mapped to the red component of the color, while the Y value gets mapped to the green component. The yellow color at the bottom right is caused by mixing the red and green components together.

In summary, the UV_to_color shader you wrote maps the UV variable to the COLOR variable. This creates a colorful gradient that represents the UV variable. It’s important to know that the UV variable represents the normalized position of a pixel as you’ll be using it a lot in the next shaders.

UV visualization

Note: UV coordinates are exactly like XY coordinates but they’re used to locate spots on a texture, not in space. The letters U and V are often used in math, geometry and physics to represent arbitrary values. They are much like how foo and bar are used in programming as placeholders.

Congrats, you created your first shader!



Source link