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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Introduction to Shaders in Godot 4
45 mins
- Getting Started
- What Is a Shader?
- Types of Shaders
- Basics of Texture Manipulation
- Fragment Shaders
- Overwriting Colors
- Manipulating Colors
- Vertex Function
- Moving Vertices
- Using Time
- Time to S(h)ine
- Selective Movement With If-Statements
- Circular Movement
- Getting the Dimensions of a Sprite
- Making Shaders Customizable
- Improving the Sway Shader
- Using Code to Set Shader Parameters
- Shader Hinting
- Combining Vertex and Fragment Functions
- Bonus Shaders
- Where to Go From Here?
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:
- The
speed_multiplier
increases the speed of the movement. -
movement_vector
is avec2
representing the limit of the movement. In this case, 25 pixels to the right. - 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. TheTIME
variable is the amount of time that has passed since the start of the program. By feeding that to thesin
function, you get an oscillating value, meaning the value will move from -1 to 1 and back in a loop. TheTIME
variable is multiplied by thespeed_multiplier
to increase the speed of the movement. Themovement_vector
is multiplied by thesin
function to determine the direction of the movement.
Save the shader to see the sprite moving back and forth.
The sin
function returns a value over time you can visualize with a 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.
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.
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.
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.
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:
-
sway_amount
is the amount of movement in the X direction. -
time_multiplier
is the speed of the movement, this will be multiplied by theTIME
variable. -
sine_wave
is the result of thesin
function, a value between -1 and 1. - The
if
statement will apply to vertices with ay
value that is less than 0.5. These are the top vertices. - Change the position of the vertex in the
x
direction by multiplying thesine_wave
value bysway_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!
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.
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:
-
speed
is the speed of the circling movement, it’s a multiplier. -
distance
is the distance the sprite moves in the X and Y direction. - Change the position of the vertex in the
x
direction by multiplying thecos
function result bydistance
. - Change the position of the vertex in the
y
direction by multiplying thesin
function result bydistance
.
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.
This effect can be used for simple circular movement without having to use the animation system.