simple glass broken

Glass Shattering Shader (Simple Screen Break Effect)

This is a simple screen-space glass shattering shader designed for Godot CanvasItem nodes (such as ColorRect or UI overlays). It creates the illusion that the screen has broken into multiple glass shards which then fall downward with gravity, rotate slightly, and scatter apart.

The shader works by sampling the current screen texture and dividing it into irregular Voronoi-like fragments. Each fragment behaves like an independent piece of glass, with its own movement delay, falling speed, and rotation.

Because it reads from the screen texture, it visually appears as if the entire screen has shattered, even though the shader is simply transforming fragments of the rendered image.

How it works

The shader performs several steps:

  1. Fragment generation
    The screen is divided into multiple cells using a Voronoi-style pattern.
    These cells represent the individual glass shards.

  2. Randomization
    Each shard receives pseudo-random values that control:

    • falling delay

    • horizontal drift

    • rotation

    • slight shape irregularities

  3. Shard transformation
    When the animation progresses, each shard:

    • falls downward using a gravity curve

    • drifts slightly sideways

    • rotates around its center

  4. Screen reconstruction
    The shader traces where each pixel originally came from and samples the screen texture accordingly, ensuring each shard displays the correct portion of the image.

  5. Edge highlighting
    A subtle glow is applied along shard borders to simulate glass edges catching light.

  6. Fade and masking
    Shards that move outside the screen gradually disappear, preventing visual artifacts.

Animation Control

The animation is controlled by a single parameter:

fall_progress (0 → 1)
Controls the progression of the shatter animation.

  • 0.0 → glass is intact

  • 1.0 → shards have fully fallen

This parameter is typically animated using a Tween or AnimationPlayer in Godot.

Adjustable Parameters

Several uniforms allow the effect to be customized:

Parameter Description
pieces_x, pieces_y Number of shards across the screen
gravity Downward falling force
scatter Horizontal spreading of shards
rotation_amount How much shards rotate
point_jitter Irregularity of shard shapes
delay_variation Random delay before shards start falling
edge_thickness Thickness of the shard edges
edge_brightness Brightness of the edge highlight
alpha_fade Amount of fading as shards fall

Typical Use Cases

This shader can be used for:

  • Screen break transitions

  • Damage or impact effectsStylized UI destruction effects

  • Glass-breaking cinematic transitions

  • or.. glass broken effects?

 

Because the effect operates entirely in a shader, it is lightweight and does not require spawning physics objects or meshes, making it suitable for real-time use.

bleep :3 uwu

Shader code
shader_type canvas_item;

uniform sampler2D screen_tex : hint_screen_texture, filter_linear_mipmap;

uniform float fall_progress : hint_range(0.0, 1.0) = 0.0;

uniform int pieces_x = 5;
uniform int pieces_y = 3;

uniform float gravity : hint_range(0.0, 4.0) = 2.2;
uniform float scatter : hint_range(0.0, 1.0) = 0.22;
uniform float rotation_amount : hint_range(0.0, 4.0) = 1.0;

uniform float point_jitter : hint_range(0.0, 0.48) = 0.34;
uniform float delay_variation : hint_range(0.0, 0.8) = 0.28;

uniform float edge_thickness : hint_range(0.0005, 0.05) = 0.01;
uniform float edge_brightness : hint_range(0.0, 2.0) = 0.35;
uniform float alpha_fade : hint_range(0.0, 1.0) = 0.0;

const int SEARCH_RADIUS = 1;

float hash12(vec2 p) {
	vec3 p3 = fract(vec3(p.xyx) * 0.1031);
	p3 += dot(p3, p3.yzx + 33.33);
	return fract((p3.x + p3.y) * p3.z);
}

vec2 hash22(vec2 p) {
	return vec2(
		hash12(p + vec2(1.23, 4.56)),
		hash12(p + vec2(7.89, 0.12))
	);
}

mat2 rot(float a) {
	float s = sin(a);
	float c = cos(a);
	return mat2(vec2(c, -s), vec2(s, c));
}

vec2 cell_point(vec2 cell_id) {
	vec2 h = hash22(cell_id);
	return cell_id + 0.5 + (h - 0.5) * (point_jitter * 2.0);
}

// Finds which Voronoi point/cell owns p
// and computes an approximate distance to the edge
void voronoi_info(vec2 p, out vec2 owner_cell, out vec2 owner_point, out float edge_dist) {
	vec2 base = floor(p);

	float best_d = 1e20;
	float second_d = 1e20;
	vec2 best_cell = vec2(0.0);
	vec2 best_point = vec2(0.0);

	for (int j = -SEARCH_RADIUS; j <= SEARCH_RADIUS; j++) {
		for (int i = -SEARCH_RADIUS; i <= SEARCH_RADIUS; i++) {
			vec2 c = base + vec2(float(i), float(j));
			vec2 pt = cell_point(c);
			float d = distance(p, pt);

			if (d < best_d) {
				second_d = best_d;
				best_d = d;
				best_cell = c;
				best_point = pt;
			} else if (d < second_d) {
				second_d = d;
			}
		}
	}

	owner_cell = best_cell;
	owner_point = best_point;
	edge_dist = second_d - best_d;
}

void piece_transform(
	vec2 piece_cell,
	out float t,
	out vec2 piece_offset_uv,
	out float piece_angle
) {
	float rnd  = hash12(piece_cell);
	float rnd2 = hash12(piece_cell + 17.37);
	float rnd3 = hash12(piece_cell + 91.11);

	float delay = rnd * delay_variation;
	t = clamp((fall_progress - delay) / max(0.0001, 1.0 - delay), 0.0, 1.0);

	float fall_y = gravity * t * t * (1.0 + rnd2 * 0.9);
	float drift_x = (rnd2 - 0.5) * scatter * t;
	float drift_y = (rnd3 - 0.5) * scatter * 0.18 * t;

	piece_offset_uv = vec2(drift_x, fall_y + drift_y);
	piece_angle = (rnd - 0.5) * rotation_amount * t;
}

void fragment() {
	vec2 uv = SCREEN_UV;
	vec2 grid = vec2(float(pieces_x), float(pieces_y));

	// Current pixel in the logical fracture space
	vec2 p_now = uv * grid;

	// Finds which shard occupies this pixel
	vec2 now_cell;
	vec2 now_point;
	float now_edge;
	voronoi_info(p_now, now_cell, now_point, now_edge);

	// Gets the transform for this shard
	float t;
	vec2 piece_offset_uv;
	float piece_angle;
	piece_transform(now_cell, t, piece_offset_uv, piece_angle);

	vec2 piece_center_uv = now_point / grid;

	// Reverses the shard transform to find the original position
	vec2 rel_now = uv - piece_center_uv;
	vec2 uv_from = piece_center_uv + rot(-piece_angle) * (rel_now - piece_offset_uv);

	// Mask for being inside the screen
	float inside_screen =
		step(0.0, uv_from.x) *
		step(0.0, uv_from.y) *
		step(uv_from.x, 1.0) *
		step(uv_from.y, 1.0);

	// Clamp to avoid sampling outside the texture
	vec2 safe_uv = clamp(uv_from, vec2(0.0), vec2(1.0));

	// Finds which shard the original position belonged to
	vec2 p_from = safe_uv * grid;
	vec2 from_cell;
	vec2 from_point;
	float from_edge;
	voronoi_info(p_from, from_cell, from_point, from_edge);

	// Only draw if the origin belongs to the same shard
	float same_piece = 1.0 - step(0.001, distance(from_cell, now_cell));

	vec4 col = texture(screen_tex, safe_uv);

	// Edge highlight between shards
	float edge = 1.0 - smoothstep(0.0, edge_thickness, now_edge);
	col.rgb += edge * edge_brightness;

	// Final fade and masks
	col.a *= inside_screen;
	col.a *= same_piece;
	col.a *= mix(1.0, 1.0 - alpha_fade, t);

	COLOR = col;
}
Live Preview
Tags
glass
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from miwls

Related shaders

guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments