Introduction to Shaders in Godot 4

Discover the art of game customization with shaders in Godot 4. Learn to craft your visual effects, from texture color manipulation to sprite animations, in this guide to writing fragment and vertex shaders. By Eric Van de Kerckhove.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 3 of 5 of this article. Click here to view the first page.

Using Time

Up to this point, the shaders you wrote were static in nature. But what if you want to animate them? That’s where time comes in!
By changing values of time, you can create interesting effects like movement, gradual color changes and more.

Time to S(h)ine

For your next shader, you’ll continuously move a sprite from left to right. To start, drag icon.svg into the viewport and name the node Move. Create a new shader for it in the vertex folder named move.gdshader and apply it to the Move node’s Material property.
Next, open the shader in the shader editor and add the following lines to the vertex() function:

float speed_multiplier = 2.0; // 1
vec2 movement_vector = vec2(25.0, 0.0); // 2

VERTEX += sin(TIME * speed_multiplier) * movement_vector; // 3

This shader code moves the sprite to the right and left of its starting position in a smooth and animated way. Here’s what the code does:

  1. The speed_multiplier increases the speed of the movement.
  2. movement_vector is a vec2 representing the limit of the movement. In this case, 25 pixels to the right.
  3. This line is the star of the show. The most important part is the sin function, which returns a value between -1 and 1 based on its input. The TIME variable is the amount of time that has passed since the start of the program. By feeding that to the sin function, you get an oscillating value, meaning the value will move from -1 to 1 and back in a loop. The TIME variable is multiplied by the speed_multiplier to increase the speed of the movement. The movement_vector is multiplied by the sin function to determine the direction of the movement.

Save the shader to see the sprite moving back and forth.

Moving sprite

The sin function returns a value over time you can visualize with a sine wave.

Sine Wave

As you can see in the illustration above, no matter what value the TIME variable has, the final value will fall between -1 and 1. This is a useful basis for creating shaders where you want values to smoothly change over time. In the next sections, you’ll be making good use of the sin function and the TIME variable to add some life into your shaders.

Selective Movement With If-Statements

Moving a sprite as a whole with a shader can be useful, but you might as well use an animation or a small script to achieve the same result. One of the benefits shaders have over the alternatives is that they can hone in on a specific part of the sprite. With if-statements, you can filter the vertices you’re interested in and move those.

You’ll be using this technique to make a tree sway in the wind. The goal here is to move the top vertices of a tree sprite, but not the bottom ones. Remember that a sprite has 4 vertices, one for each corner.

Tree with green top vertices

Another variable to keep in mind is your old friend UV, which also works in vertex shaders as each vertex has its own UV coordinate.

UV visualization

By combining these two variables, you can filter the vertices you need! Any vertex with a UV coordinate that has a Y value smaller than 0.5 will be one of the top vertices. I’ve added a visualization below to show you how it works. Only vertices in the green area should be moved.

Tree UV visualization

With the theory covered, it’s time to write the shader. To get started, drag a tree.png sprite from the textures folder into the viewport and name the node Sway.

Tree

Create a new shader for it in the vertex folder named sway.gdshader and apply it to the Sway node’s Material property. Open the new shader in the shader editor and only keep the vertex function.

Next, add the following code to the vertex() function:

float sway_amount = 20.0; // 1
float time_multiplier = 4.0; // 2
float sine_wave = sin(TIME * time_multiplier); // 3

if (UV.y < 0.5) { // 4
    VERTEX += vec2(sine_wave * sway_amount, 0); // 5
}

Here’s a summary of the code:

  1. sway_amount is the amount of movement in the X direction.
  2. time_multiplier is the speed of the movement, this will be multiplied by the TIME variable.
  3. sine_wave is the result of the sin function, a value between -1 and 1.
  4. The if statement will apply to vertices with a y value that is less than 0.5. These are the top vertices.
  5. Change the position of the vertex in the x direction by multiplying the sine_wave value by sway_amount.

This shader is similar to the previous one you wrote, but instead of moving all vertices, the if-statement makes sure that just the top vertices are moved. Save the shader and its effect will become clear when you look at the tree sprite. It’s now swaying back and forth. Must be a windy day!

Tree sway

Next up is going a step further with the sin function by adding its sibling cos in the mix.

Circular Movement

You’ve been using the sin function with great effect to create linear movement. There’s another function you can combine with the sin function to create circular movement: the cos function. Both are trigonometric functions, meaning they’re used in the calculations of angles. Both functions return a value between -1 and 1 based on the input, an angle in radians.

In simpler terms, the cos function returns the X coordinate of a unit circle, and the sin function returns the Y coordinate. A unit circle is a circle with a radius of 1. Take a look at the visualization below to make the concept easier to understand.

Cos and Sin function visualization

Focus on the green circle at the bottom right, this is the unit circle. The smaller yellow circle inside is the input value.
The green dot that moves around on it is the final position. This position is influenced by the cos and sin functions, where the blue dot is the result of the cos function and the red dot is the result of the sin function. Both functions create smooth waves over time and given the same input, they match up to give coordinates on the unit circle.

To test this out with a shader, drag another icon.svg sprite into the viewport and name the node Circle. Now create a new shader in the vertex folder named circling.gdshader and apply it to the Circle node’s Material property. Open this new shader in the shader editor and add the following code to the vertex() function:

float speed = 5.0; // 1
float distance = 15.0; // 2

VERTEX.x += cos(TIME * speed) * distance; // 3
VERTEX.y += sin(TIME * speed) * distance; // 4

This is what the code is doing:

  1. speed is the speed of the circling movement, it’s a multiplier.
  2. distance is the distance the sprite moves in the X and Y direction.
  3. Change the position of the vertex in the x direction by multiplying the cos function result by distance.
  4. Change the position of the vertex in the y direction by multiplying the sin function result by distance.

As you can see, the code isn’t complex, but the effect is nice! Save the shader and take a look at the Circle node, it’s now circling around.

Circular movement

This effect can be used for simple circular movement without having to use the animation system.