Shaders from GDScript

Godot shaders use a GLSL-like language in .gdshader files.

32.1 Godot shading language

Godot shaders use a GLSL-like language in .gdshader files. Each shader declares a type:

2D sprites, UI, tilemaps GPU particle processing Volumetric fog volumes

// res://shaders/hit_flash.gdshader
shader_type canvas_item;

uniform vec4 flash_color : source_color = vec4(1.0);
uniform float flash_amount : hint_range(0.0, 1.0) = 0.0;

void fragment() {
	vec4 tex = texture(TEXTURE, UV);
	COLOR = vec4(mix(tex.rgb, flash_color.rgb, flash_amount), tex.a);
}

32.2 Controlling shaders from GDScript

@onready var sprite: Sprite2D = $Sprite2D
var mat: ShaderMaterial

func _ready() -> void:
	mat = sprite.material as ShaderMaterial
	# If several instances share the material, make it unique first:
	# sprite.material = sprite.material.duplicate()  (or enable resource_local_to_scene)

func hit_flash() -> void:
	mat.set_shader_parameter("flash_amount", 1.0)
	var tween := create_tween()
	tween.tween_method(func(v: float): mat.set_shader_parameter("flash_amount", v), 1.0, 0.0, 0.2)
	# or tween the property path directly:
	# tween.tween_property(mat, "shader_parameter/flash_amount", 0.0, 0.2)

func set_up_in_code() -> void:
	var shader := preload("res://shaders/hit_flash.gdshader")
	var m := ShaderMaterial.new()
	m.shader = shader
	m.set_shader_parameter("flash_color", Color.RED)
	sprite.material = m
	print(m.get_shader_parameter("flash_amount"))

32.3 3D example: dissolve

shader_type spatial;

uniform sampler2D albedo_tex : source_color;
uniform sampler2D noise_tex;
uniform float dissolve : hint_range(0.0, 1.0) = 0.0;
uniform vec4 edge_color : source_color = vec4(1.0, 0.5, 0.0, 1.0);

void fragment() {
	float n = texture(noise_tex, UV).r;
	if (n < dissolve) {
		discard;
	}
	ALBEDO = texture(albedo_tex, UV).rgb;
	EMISSION = edge_color.rgb * step(n, dissolve + 0.05) * 4.0;
}
func dissolve_out(mesh: MeshInstance3D) -> void:
	var m := mesh.get_active_material(0) as ShaderMaterial
	create_tween().tween_property(m, "shader_parameter/dissolve", 1.0, 1.5)

32.4 Global and per-instance uniforms

global uniform vec3 wind_direction;     // defined in Project Settings → Globals → Shader Globals
instance uniform vec4 tint : source_color = vec4(1.0);   // spatial shaders: per-instance value
RenderingServer.global_shader_parameter_set("wind_direction", Vector3(1, 0, 0.5))
$Tree.set_instance_shader_parameter("tint", Color.GREEN)   # GeometryInstance3D, no material duplication

32.5 Visual shaders and other options

VisualShader: node-based editor producing the same shaders; good for artists.

Convert a StandardMaterial3D to a ShaderMaterial (right-click → Convert) to see generated code.

Screen-reading effects: uniform sampler2D screen_tex : hint_screen_texture; in a full-screen ColorRect on a CanvasLayer for post-processing.

CompositorEffect (4.3+) for custom rendering passes in Forward+/Mobile.

32.6 Compute shaders (RenderingDevice)

Available with the Forward+ and Mobile renderers (not Compatibility).

Godot 3 note: set_shader_param() became set_shader_parameter(); hint_color became source_color; SCREEN_TEXTURE became a hint_screen_texture uniform.

// res://compute/double.glsl
#[compute]
#version 450

layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;

layout(set = 0, binding = 0, std430) restrict buffer DataBuffer {
	float data[];
} buf;

void main() {
	buf.data[gl_GlobalInvocationID.x] *= 2.0;
}
func run_compute() -> void:
	var rd := RenderingServer.create_local_rendering_device()
	var spirv: RDShaderSPIRV = load("res://compute/double.glsl").get_spirv()
	var shader := rd.shader_create_from_spirv(spirv)

	var input := PackedFloat32Array()
	input.resize(64)
	for i in 64: input[i] = i
	var bytes := input.to_byte_array()
	var buffer := rd.storage_buffer_create(bytes.size(), bytes)

	var uniform := RDUniform.new()
	uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER
	uniform.binding = 0
	uniform.add_id(buffer)
	var uniform_set := rd.uniform_set_create([uniform], shader, 0)

	var pipeline := rd.compute_pipeline_create(shader)
	var list := rd.compute_list_begin()
	rd.compute_list_bind_compute_pipeline(list, pipeline)
	rd.compute_list_bind_uniform_set(list, uniform_set, 0)
	rd.compute_list_dispatch(list, 1, 1, 1)     # 1 workgroup × 64 invocations
	rd.compute_list_end()
	rd.submit()
	rd.sync()

	var output := rd.buffer_get_data(buffer).to_float32_array()
	print(output.slice(0, 8))                    # [0, 2, 4, 6, 8, 10, 12, 14]
	rd.free_rid(buffer)
	rd.free()