Godot Shaders (4.x)
Write canvas_item (2D) and spatial (3D) shaders in the Godot Shading Language, animate
with TIME/UV, expose uniforms, and read the screen. Targets Godot 4.7.
When to use
- Use when writing
.gdshader code or a ShaderMaterial: 2D effects (outline, dissolve,
flash, water), 3D surface shaders (rim light, toon, scrolling UV), or screen-space
post effects.
When not to use: the cross-engine concepts of shading (UVs, vertex/fragment
theory) → shader-programming; particles/VFX nodes → general 3D; non-shader visuals.
Core workflow
- Pick the shader type on the first line:
shader_type canvas_item; for 2D
(Sprite2D, TextureRect, anything CanvasItem) or shader_type spatial; for 3D
materials. (particles, sky, fog also exist.)
- Attach via a
ShaderMaterial. Create a ShaderMaterial, assign your .gdshader,
and put it on the node's material. Uniforms appear in the Inspector.
- Write
fragment() to set the output: COLOR (2D) or ALBEDO/EMISSION/ALPHA
(3D). Optionally vertex() to move geometry and light() for custom lighting.
- Expose tunables as
uniforms with hints (source_color, hint_range) so they are
editable and correctly color-managed.
- Animate with the built-in
TIME and sample textures with texture(tex, UV).
- Set uniforms from code with
material.set_shader_parameter("name", value).
Patterns
1. 2D (canvas_item): tint + scrolling UV
shader_type canvas_item;
uniform vec4 tint : source_color = vec4(1.0); // source_color = sRGB-correct color
uniform float scroll_speed : hint_range(0.0, 2.0) = 0.3;
void fragment() {
vec2 uv = UV;
uv.x += TIME * scroll_speed; // scroll horizontally over time
COLOR = texture(TEXTURE, uv) * tint; // TEXTURE = the node's texture
}
2. 2D dissolve using a noise threshold
shader_type canvas_item;
uniform sampler2D noise : repeat_enable; // a NoiseTexture2D
uniform float amount : hint_range(0.0, 1.0) = 0.0;
void fragment() {
vec4 tex = texture(TEXTURE, UV);
float n = texture(noise, UV).r;
if (n < amount) {
discard; // cut the pixel away
}
COLOR = tex;
}
3. 3D (spatial): emissive rim light
shader_type spatial;
uniform vec4 base_color : source_color = vec4(0.2, 0.5, 1.0, 1.0);
uniform vec3 rim_color : source_color = vec3(0.6, 0.8, 1.0);
uniform float rim_power : hint_range(0.5, 8.0) = 3.0;
void fragment() {
ALBEDO = base_color.rgb;
// VIEW and NORMAL are view-space built-ins; rim is strong at grazing angles.
float rim = pow(1.0 - dot(NORMAL, VIEW), rim_power);
EMISSION = rim_color * rim;
}
4. Screen-reading post effect (4.x hint, not SCREEN_TEXTURE)
shader_type canvas_item;
// 4.x: declare the screen as a uniform with hint_screen_texture.
uniform sampler2D screen_tex : hint_screen_texture, filter_linear_mipmap;
uniform float blur : hint_range(0.0, 4.0) = 1.0;
void fragment() {
vec2 px = SCREEN_PIXEL_SIZE * blur;
vec4 c = texture(screen_tex, SCREEN_UV);
c += texture(screen_tex, SCREEN_UV + vec2(px.x, 0.0));
c += texture(screen_tex, SCREEN_UV - vec2(px.x, 0.0));
COLOR = c / 3.0;
}
Set a uniform from GDScript:
$Sprite2D.material.set_shader_parameter("amount", 0.7)
Pitfalls
- 3.x → 4.x renames.
SCREEN_TEXTURE is removed — declare
uniform sampler2D x : hint_screen_texture; and sample with SCREEN_UV. Color hints
hint_color→source_color; hint_albedo/hint_white→source_color;
hint_range stays. Depth/normal use hint_depth_texture / hint_normal_roughness_texture.
- Wrong output variable. In
canvas_item write COLOR; in spatial write ALBEDO
(and EMISSION, ALPHA, ROUGHNESS, METALLIC). Writing COLOR in a spatial shader
does nothing.
- Color uniforms without
source_color are treated as raw linear values and look
wrong (washed/dark) because Godot won't sRGB-convert them.
- Transparency needs opt-in (3D). For
ALPHA < 1.0 to blend, add a render mode or set
the material transparency; otherwise it's opaque/cut.
- Sampling outside [0,1] UV without
repeat_enable clamps. Add : repeat_enable to
the sampler uniform for tiling/scroll.
TIME is seconds since start and keeps growing — wrap with fract()/mod() for
periodic effects to avoid precision drift.
discard is costly on some hardware and breaks early-Z; prefer setting ALPHA/
COLOR.a when you can.
References
- For built-in variables per shader type, render modes,
varying, custom light(),
vertex() displacement, and the visual shader graph, read
references/shading-language.md.
Related skills
shader-programming — engine-agnostic shader concepts (GLSL/HLSL).
godot-3d-essentials — materials, environment, and where spatial shaders live.
godot-ui-control — applying shaders to UI for effects.
1---2name: godot-shaders3description: Write Godot 4.7 shaders in the Godot Shading Language: canvas_item shaders for 2D and spatial shaders for 3D, with vertex/fragment functions, uniforms (source_color, hint_range), TIME/UV animation, and screen-reading via hint_screen_texture. Use when authoring .gdshader files, writing fragment/vertex code, making 2D/3D visual effects, or porting 3.x shaders (SCREEN_TEXTURE, hint_color) to 4.x.4---5
6# Godot Shaders (4.x)
7
8Write `canvas_item` (2D) and `spatial` (3D) shaders in the Godot Shading Language, animate
9with `TIME`/`UV`, expose `uniform`s, and read the screen. Targets **Godot 4.7**.
10
11## When to use
12
13- Use when writing `.gdshader` code or a `ShaderMaterial`: 2D effects (outline, dissolve,
14 flash, water), 3D surface shaders (rim light, toon, scrolling UV), or screen-space
15 post effects.
16
17**When *not* to use:** the cross-engine *concepts* of shading (UVs, vertex/fragment
18theory) → `shader-programming`; particles/VFX nodes → general 3D; non-shader visuals.
19
20## Core workflow
21
221. **Pick the shader type** on the first line: `shader_type canvas_item;` for 2D
23 (Sprite2D, TextureRect, anything `CanvasItem`) or `shader_type spatial;` for 3D
24 materials. (`particles`, `sky`, `fog` also exist.)
252. **Attach via a `ShaderMaterial`.** Create a `ShaderMaterial`, assign your `.gdshader`,
26 and put it on the node's `material`. Uniforms appear in the Inspector.
273. **Write `fragment()`** to set the output: `COLOR` (2D) or `ALBEDO`/`EMISSION`/`ALPHA`
28 (3D). Optionally `vertex()` to move geometry and `light()` for custom lighting.
294. **Expose tunables as `uniform`s** with hints (`source_color`, `hint_range`) so they are
30 editable and correctly color-managed.
315. **Animate with the built-in `TIME`** and sample textures with `texture(tex, UV)`.
326. **Set uniforms from code** with `material.set_shader_parameter("name", value)`.
33
34## Patterns
35
36### 1. 2D (canvas_item): tint + scrolling UV
37
38```glsl
39shader_type canvas_item;
40
41uniform vec4 tint : source_color = vec4(1.0); // source_color = sRGB-correct color
42uniform float scroll_speed : hint_range(0.0, 2.0) = 0.3;
43
44void fragment() {
45 vec2 uv = UV;
46 uv.x += TIME * scroll_speed; // scroll horizontally over time
47 COLOR = texture(TEXTURE, uv) * tint; // TEXTURE = the node's texture
48}
49```
50
51### 2. 2D dissolve using a noise threshold
52
53```glsl
54shader_type canvas_item;
55
56uniform sampler2D noise : repeat_enable; // a NoiseTexture2D
57uniform float amount : hint_range(0.0, 1.0) = 0.0;
58
59void fragment() {
60 vec4 tex = texture(TEXTURE, UV);
61 float n = texture(noise, UV).r;
62 if (n < amount) {
63 discard; // cut the pixel away
64 }
65 COLOR = tex;
66}
67```
68
69### 3. 3D (spatial): emissive rim light
70
71```glsl
72shader_type spatial;
73
74uniform vec4 base_color : source_color = vec4(0.2, 0.5, 1.0, 1.0);
75uniform vec3 rim_color : source_color = vec3(0.6, 0.8, 1.0);
76uniform float rim_power : hint_range(0.5, 8.0) = 3.0;
77
78void fragment() {
79 ALBEDO = base_color.rgb;
80 // VIEW and NORMAL are view-space built-ins; rim is strong at grazing angles.
81 float rim = pow(1.0 - dot(NORMAL, VIEW), rim_power);
82 EMISSION = rim_color * rim;
83}
84```
85
86### 4. Screen-reading post effect (4.x hint, not SCREEN_TEXTURE)
87
88```glsl
89shader_type canvas_item;
90
91// 4.x: declare the screen as a uniform with hint_screen_texture.
92uniform sampler2D screen_tex : hint_screen_texture, filter_linear_mipmap;
93uniform float blur : hint_range(0.0, 4.0) = 1.0;
94
95void fragment() {
96 vec2 px = SCREEN_PIXEL_SIZE * blur;
97 vec4 c = texture(screen_tex, SCREEN_UV);
98 c += texture(screen_tex, SCREEN_UV + vec2(px.x, 0.0));
99 c += texture(screen_tex, SCREEN_UV - vec2(px.x, 0.0));
100 COLOR = c / 3.0;
101}
102```
103
104Set a uniform from GDScript:
105
106```gdscript
107$Sprite2D.material.set_shader_parameter("amount", 0.7)
108```
109
110## Pitfalls
111
112- **3.x → 4.x renames.** `SCREEN_TEXTURE` is removed — declare
113 `uniform sampler2D x : hint_screen_texture;` and sample with `SCREEN_UV`. Color hints
114 `hint_color`→`source_color`; `hint_albedo`/`hint_white`→`source_color`;
115 `hint_range` stays. Depth/normal use `hint_depth_texture` / `hint_normal_roughness_texture`.
116- **Wrong output variable.** In `canvas_item` write `COLOR`; in `spatial` write `ALBEDO`
117 (and `EMISSION`, `ALPHA`, `ROUGHNESS`, `METALLIC`). Writing `COLOR` in a spatial shader
118 does nothing.
119- **Color uniforms without `source_color`** are treated as raw linear values and look
120 wrong (washed/dark) because Godot won't sRGB-convert them.
121- **Transparency needs opt-in (3D).** For `ALPHA < 1.0` to blend, add a render mode or set
122 the material transparency; otherwise it's opaque/cut.
123- **Sampling outside [0,1] UV** without `repeat_enable` clamps. Add `: repeat_enable` to
124 the sampler uniform for tiling/scroll.
125- **`TIME` is seconds since start** and keeps growing — wrap with `fract()`/`mod()` for
126 periodic effects to avoid precision drift.
127- **`discard` is costly** on some hardware and breaks early-Z; prefer setting `ALPHA`/
128 `COLOR.a` when you can.
129
130## References
131
132- For built-in variables per shader type, render modes, `varying`, custom `light()`,
133 `vertex()` displacement, and the visual shader graph, read
134 `references/shading-language.md`.
135
136## Related skills
137
138- `shader-programming` — engine-agnostic shader concepts (GLSL/HLSL).
139- `godot-3d-essentials` — materials, environment, and where spatial shaders live.
140- `godot-ui-control` — applying shaders to UI for effects.