Learn Godot shaders

Godot has its own shading language: GLSL-like, with built-ins such as UV, COLOR and TIME. Practise the ideas here, then export a .gdshader for a ColorRect or Sprite. Because it is so close to GLSL, everything you learn from the GLSL lessons carries over.

Things to know about Godot shading language

  • A canvas shader starts with shader_type canvas_item; and writes the color to COLOR in fragment().
  • UV starts at the top-left, so the lesson functions receive a flipped y. The starter code does it for you.
  • Use TIME for seconds and uniform for values you set from the editor or from GDScript.
  • The shader_type line and the fragment() wrapper do not count towards your code length.

Cheat sheet

IdeaWrite
Vectorsvec2 vec3 vec4
Declare a valuefloat d = length(p);
A functionfloat circle(vec2 p, float r) { ... }
Matricesmat2 mat3, multiply with M * v (this site writes v * M for lesson rotations)
Blendmix(a, b, t)
Fractional partfract(x)
Clamp to 0..1clamp(x, 0.0, 1.0)
Remaindermod(a, b) (always positive for positive b)
Choosec ? a : b
Angle of a vectoratan(y, x)
Time in secondsTIME
Pixel positionUV (0..1, top-left)
Width / heightaspect
Entry pointvoid fragment() { COLOR = …; }

Handy built-ins: sin cos abs sign floor ceil fract step smoothstep clamp min max pow exp sqrt length distance dot cross normalize reflect

Gotchas

  • Every pixel runs on its own, with no memory of other pixels.
  • Declare a function above the place you use it.
  • Prefer 2.0 over 2 in maths. This site fixes most cases for you; real engines may not.
  • Loops need a small fixed count: for (int i = 0; i < 5; i++).
  • Godot's UV starts at the top-left. The lessons' frag(uv) uses bottom-left, which is why the starter flips it. The wrapper doesn't count towards your score.

Example: a glowing ring

The same shader as it is written for Godot:

shader_type canvas_item;

vec4 frag(vec2 uv)
{
    vec2 p = (uv - 0.5) * vec2(aspect, 1.0);
    float d = length(p);
    float m = 1.0 - smoothstep(0.0, 0.02, abs(d - 0.3));
    vec3 bg = vec3(0.08, 0.08, 0.12);
    vec3 fg = vec3(0.4, 0.9, 1.0);
    return vec4(mix(bg, fg, m), 1.0);
}

void fragment()
{
    // Godot's UV starts at the top-left, the lessons use bottom-left
    COLOR = frag(vec2(UV.x, 1.0 - UV.y));
}

Exporting to Godot

In Godot 4: add a ColorRect, give it a new ShaderMaterial, create a Shader on it and replace its code with this. Change aspect to your rect's width / height.

shader_type canvas_item;

uniform float aspect = 1.3333;

vec4 frag(vec2 uv)
{
    vec2 p = (uv - 0.5) * vec2(aspect, 1.0);
    float d = length(p);
    float m = 1.0 - smoothstep(0.0, 0.02, abs(d - 0.3));
    vec3 bg = vec3(0.08, 0.08, 0.12);
    vec3 fg = vec3(0.4, 0.9, 1.0);
    return vec4(mix(bg, fg, m), 1.0);
}

void fragment()
{
    // Godot's UV starts at the top-left, the lessons use bottom-left
    COLOR = frag(vec2(UV.x, 1.0 - UV.y));
}

Questions

Is this Godot 4 or Godot 3?

The export is for Godot 4. Most of the language is the same in Godot 3 apart from small changes to built-in names.

Other languages

Unity (Built-in) · Unity (URP) · Unreal · Shadertoy / WebGL · OpenGL (LWJGL, raylib) · three.js · GameMaker · LÖVE (Lua) · Apple (iOS, macOS) · WebGPU / Bevy