Highly customisable moving voronoi diagram transition
Use on a CanvasItem
Features:
- Can be used as a transition effect, changing the circles radii
- Turn on/off center visualization
- Turn on/off movement
- Visualize borders
- Use of Minkowski distance
Shader code
shader_type canvas_item;
uniform float radius : hint_range(0.0, 1.0, 0.01) = 0.45;
uniform int center_number : hint_range(1, 1024, 1) = 100;
uniform sampler2D circle_color_gradient; // GradientTexture2D
uniform float seed : hint_range(0.0, 1.0, 0.01) = 0.0;
uniform bool draw_centers = false;
uniform bool draw_borders = false;
uniform vec4 border_color : source_color = vec4(vec3(0.0),1.0);
uniform float center_radius : hint_range(0.0, 1.0, 0.001)= 0.005;
uniform sampler2D center_color_gradient;
uniform bool move = true;
uniform float speed : hint_range(0.0, 1.0, 0.001)= 0.02;
uniform float outline_thickness_mult = 1.5;
uniform float minimum_thickness : hint_range(0.0, 0.01, 0.0001) = 0.0;
uniform vec4 background_color : source_color = vec4(0.0); // only visible with small circles
uniform float minkowski_distance_p = 2.0;
// generic random function
float random(vec2 uv) {
return fract(sin(dot(uv, vec2(12.9898, 78.233)) + seed) * 43758.5453);
}
float minkowski_distance(vec2 p1, vec2 p2, float p){
return pow(pow(abs(p1.x-p2.x),p) + pow(abs(p1.y-p2.y),p), 1.0/p);
}
void fragment() {
vec2 uv = UV;
vec2 deriv = fwidthFine(uv); // not available with compatibility renderer
float aspect_ratio = deriv.y / deriv.x;
uv.x *= aspect_ratio;
float best_dist = 1e9;
float second_best_dist = 1e9;
vec2 best_center = vec2(0.0);
// my GPU hates me
for (int i = 0; i < center_number; i++) {
float fi = float(i);
vec2 base = vec2(random(vec2(fi, 0.0)), random(vec2(fi, 1.0))); // random coord for center of circle
vec2 raw;
if (move) {
float angle = random(vec2(fi, 2.0)) * TAU; // random angle for movement in radians
raw = base + vec2(cos(angle), sin(angle)) * (speed * TIME);
raw = fract(raw);
} else {
raw = base;
}
// Every circle also has "copies" at every integer offset.
// We only need offsets for -1, 0, +1 in x and y (the 3×3 block)
// because circles in further offsets will never be shown.
for (int xi = -1; xi <= 1; xi++) {
for (int yi = -1; yi <= 1; yi++) {
// get shifted center
vec2 candidate = raw + vec2(float(xi), float(yi));
candidate.x *= aspect_ratio;
float d = minkowski_distance(uv, candidate,minkowski_distance_p);
if (d < best_dist) {
second_best_dist = best_dist;
best_dist = d;
best_center = candidate;
}else if (d < second_best_dist) {
second_best_dist = d;
}
}
}
}
if (best_dist < radius) {
vec4 color;
vec2 best_center_corrected = vec2(best_center.x / aspect_ratio, best_center.y);
vec2 uv_corrected = vec2(uv.x / aspect_ratio, uv.y);
// paint pixel according to the color of the closest center
COLOR = texture(circle_color_gradient,best_center_corrected);
if (draw_borders) {
float d = second_best_dist - best_dist;
float edge_thickness = fwidth(d) * outline_thickness_mult + minimum_thickness;
if (d < edge_thickness) {
COLOR = border_color;
}
}
if (draw_centers && best_dist < center_radius) {
COLOR = texture(center_color_gradient, vec2(uv.x / aspect_ratio, uv.y));
}
} else {
COLOR = background_color;
}
}




Fantastic! Works great!