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 2 of 5 of this article. Click here to view the first page.

Overwriting Colors

For your next shader, you’ll be manipulating the color of a sprite. Repeat the steps you did for the previous shader. First, create a new shader named solid_color.gdshader in the shaders/fragment folder. Next, drag another copy of icon.svg from the FileSystem into the viewport and name it White. Now apply the new shader to it by dragging the shader to its Material property.

A sprite

Open the solid_color shader in the shader editor to start working on it. This time, the original colors of the sprite should be overwritten by pure white while keeping the alpha intact. You already know how to change the color of a pixel using the COLOR variable, but for this shader you’ll need to know the original color of the pixel first. To do so, add the following line to the top of the fragment() function, inside its curly brackets:

vec4 color = texture(TEXTURE, UV);

This stores the color of the current pixel in a vec4 variable called color. The texture function is a built-in function that performs a texture read at a specific UV coordinate. The TEXTURE variable is a built-in variable that represents the texture of the current sprite. This function returns a pixel color in RGBA format.

Note: Are you wondering where all these built-in variables and functions are coming from? Godot’s official documentation includes a list of built-in canvas item variables and shader functions.

Now that you have access to the original color, you can define what color you’d like the pixel to be using a variable. Add this line to the fragment() function:

vec4 new_color = vec4(1.0, 1.0, 1.0, 1.0);

This stores a pure white color in a vec4 variable called new_color. To apply this new color to the pixel, add these lines to the fragment() function:

color.r = new_color.r;
color.g = new_color.g;
color.b = new_color.b;

This overwrites the original red, green and blue components of the pixel with the ones in the new_color variable. Note that the alpha component is still intact as you don’t overwrite it.
To actually change the pixel color, set the COLOR variable like before:

COLOR = color;

Now save the shader using the CTRL/CMD-S key combination and look at the sprite again. It should look like a solid white color, while keeping its shape intact.

White sprite

A shader like this can be useful to keep the identity of a character hidden by overwriting its color with black for example. Another use case would be to flash a sprite when it gets hit.

Manipulating Colors

In this section, you’ll learn how to manipulate the color of a sprite for a practical use case: making it grayscale. You can use this effect for flashbacks or for dramatic moments in your game. Like before, create a new shader named grayscale.gdshader in the shaders/fragment folder. Drag a copy of icon.svg into the viewport and name it Grayscale. Now apply the new shader to it by dragging the shader to its Material property.

Open the shader and think how you’d make the pixel colors grayscale. An often used method is to take the average of the red, green and blue components of the pixel and average them. I recommend you to try to create this shader yourself before moving on. Use the code of the previous shader and try to change it to make the sprite grayscale.

I hope you gave it a shot! No worries if you didn’t or if it didn’t work as planned, I’ll explain how to do it below.

To create a grayscale shader, add the following code to the fragment() function:

vec4 input_color = texture(TEXTURE, UV); // 1
float gray = 0.21 * input_color.r + 0.71 * input_color.g + 0.07 * input_color.b; // 2
vec4 output_color = vec4(gray, gray, gray, input_color.a); // 3
COLOR = output_color; // 4

Instead of using the average of the RGB components, this uses the luminosity method to create a grayscale effect. This is a weighted average based on human color perception. Our eyes are more sensitive to green colors than to red or blue so its value is weighted more. This method gives a more natural grayscale effect.
Here’s how the shader works:

  1. Store the pixel color in a vec4 variable called input_color.
  2. Calculate the grayscale value using the luminosity method and store it in a float variable called gray.
  3. Create a new color named output_color and give all of its components the value of gray, excluding the alpha component.
  4. Apply the new color to the COLOR variable.

The sprite should now be a nice grayscale color.

Grayscale sprite

Vertex Function

Until now you’ve been using the fragment function to manipulate the color of the sprite. But what if you want to manipulate the position of the sprite? That’s where the vertex function comes in. You can use this function to move, rotate and scale the vertices of a sprite in your shader. This is useful to create cool effects and animations.

Moving Vertices

To start creating your first vertex shader, create a new folder in the shaders folder named vertex. These folders aren’t necessary to create a working shader, but they do help with organizing your shaders.

Now create a new shader in the vertex folder named offset.gdshader. Use the same parameters like you did with the previous shaders.

Offset shader

Next, drag icon.svg into the viewport and name the node Offset. Drag the offset shader to its Material property and open the shader in the shader editor.

Take a closer look at the shader. Its vertex function is a processor function like fragment. Unlike its counterpart, the GPU will call it for each vertex of the sprite instead of each pixel. By default, canvas items are a quad and have four vertices, one at each corner. Each of these vertices have a position and a UV coordinate.

Vertex illustration

To change the position of a vertex, you modify the value the VERTEX variable. To give this a try, add the following line inside of the vertex() function:

VERTEX += vec2(25.0, 0.0);

Like the COLOR in a fragment function, VERTEX is a built-in variable. It’s a vec2 with 2 floating-point components representing the X and Y coordinates of the vertex. As it gets called for every vertex, the line above shifts all the vertices 25 pixels to the right of their original position.
You can see the change after saving the shader and selecting the sprite in the editor. The sprite will clearly be offset to the right from its selection box.

Sprite offset