Animated Cellular Grid
This is a dynamic, abstract shader adapted for Godot 4 that creates a cellular grid of pulsating, randomized circles. The pattern is highly interactive and suitable for use as a technological background, visualizer, or screen saver.
The shader works by:
-
Grid Tiling: Tiling the UV space to create distinct cells (
id). -
Randomization: Using a custom randomized hash function (
Rancol) to assign unique, pulsating colors to each cell. -
Warping: Applying a time-based vertical warp to the grid lines, creating a fluid, rain-like or waterfall effect.
-
Interaction: The random seed of the entire animation is offset by the mouse position, allowing the user to subtly change the pattern and colors in real-time.
The accompanying GDScript ensures that the mouse and resolution data are accurately passed to the shader every frame.
Adjustable Uniforms (Shader Parameters):
| Parameter | Type | Controlled By | Description |
| speed_multiplier | float |
Inspector | Controls the overall animation speed of the pulsating circles and the vertical grid warp. |
| enable_background | bool |
Inspector | Toggles the visibility of the solid background color. |
| background_color | vec4 |
Inspector | Sets the base color used for the background when enabled. |
| resolution | vec2 |
GDScript | (Input) The size of the viewport; used for correct aspect ratio calculation. |
| mouse_pos | vec2 |
GDScript |
(Input) The global screen position of the mouse pointer, used to seed the pattern’s randomness.
|
extends ColorRect
func _process(delta):
if material is ShaderMaterial:
# Usamos la función de Godot 4
material.set_shader_parameter("resolution", get_viewport_rect().size)
material.set_shader_parameter("mouse_pos", get_global_mouse_position())
Shader code
shader_type canvas_item;
uniform vec2 resolution;
uniform vec2 mouse_pos;
uniform float speed_multiplier = 1.0;
uniform bool enable_background = true;
uniform vec4 background_color = vec4(0.0, 0.0, 0.0, 1.0);
float No(float x, vec2 T){
return fract(9667.5 * sin(7983.75 * (x + T.x) + 297. + T.y));
}
vec4 Rancol(vec2 x, vec2 T){
return vec4(No(x.x + x.y, T), No(x.x*x.x + x.y, T), No(x.x*x.x + x.y*x.y, T), 1.0);
}
vec4 grid(vec2 uv, float t, vec2 T){
vec4 C1 = vec4(0.0);
vec4 C2 = vec4(0.0);
uv *= 20.0;
vec2 id = vec2(floor(uv.x), floor(uv.y));
uv.y += (5.0 * No(id.x * id.x, T) + 1.0) * t * 0.04;
uv.y += No(id.x, T);
id = vec2(floor(uv.x), floor(uv.y));
uv = fract(uv) - 0.5;
float d = length(uv);
float t_local = t * (1.0 * No(id.x + id.y, T));
float r = 0.1 * sin(t_local + sin(t_local) * 0.5) + 0.3;
float r1 = 0.07 * sin(2.0 * t_local + sin(2.0 * t_local) * 0.5) + 0.1 * No(id.x + id.y, T);
if (d < r && d > r - 0.1) {
C2 = 0.5 * Rancol(id + vec2(1.0), T) + vec4(0.5);
C2 *= smoothstep(r - 0.12, r, d);
C2 *= 1.0 - smoothstep(r - 0.05, r + 0.12, d);
}
if (d < r1) {
C2 = 0.5 * Rancol(id + vec2(1.0), T) + vec4(0.5);
}
return C2 + C1;
}
void fragment() {
vec2 uv = FRAGCOORD.xy / resolution.xy;
uv.y *= resolution.y / resolution.x;
float t = TIME * speed_multiplier;
vec2 T = mouse_pos / resolution;
vec4 final_color = grid(uv, t, T);
if (enable_background) {
final_color += background_color;
}
COLOR = final_color;
}

