Impact Glass Shader

Impact Glass Shader

What is it?

A procedural glass crack effect that creates realistic fractures using Voronoi patterns. Click anywhere on the glass – it cracks with optical refraction, blur, and glow, then automatically disappears after a set duration. Perfect for UI feedback, damage indicators, or glass-breaking mechanics.

How to use:

  1. Add a ColorRect node to your scene

  2. Create a new ShaderMaterial and assign the glassshatter.gdshader

  3. Attach this script to the ColorRect:

extends ColorRect

## How long the crack stays visible before disappearing (seconds)
@export var crack_duration: float = 1.0
## Strength of refraction/distortion effect
@export var refraction_strength: float = 4.0
## Amount of blur around crack lines
@export var blur_amount: float = 1.2
## Tint strength of the glass material
@export var glass_tint: float = 0.06
## Glow intensity of the crack
@export var crack_glow: float = 0.8
## Number of crack points (higher = more complex cracks)
@export var point_count: int = 50
@export_range(100, 1000) var crack_radius: float = 700.0
@export_range(0.1, 3.0) var crack_thickness: float = 1.0
## Sound when cracking
@export var crack_sound: AudioStreamPlayer = null

var crack_timer: float = 0.0
var is_cracked: bool = false

func _ready():
	mouse_filter = MOUSE_FILTER_STOP
	_set_crack_visible(false)
	_update_shader_parameters()

func _update_shader_parameters():
	material.set_shader_parameter("refraction_strength", refraction_strength)
	material.set_shader_parameter("blur_amount", blur_amount)
	material.set_shader_parameter("glass_tint", glass_tint)
	material.set_shader_parameter("crack_glow", crack_glow)
	material.set_shader_parameter("point_count", point_count)
	material.set_shader_parameter("crack_radius", crack_radius)
	material.set_shader_parameter("crack_thickness", crack_thickness)

@warning_ignore("shadowed_variable_base_class")
func _set_crack_visible(visible: bool):
	material.set_shader_parameter("show_crack", visible)
	if not visible:
		is_cracked = false

func _gui_input(event):
	if event is InputEventMouseButton:
		if event.pressed and (event.button_index == MOUSE_BUTTON_LEFT or event.button_index == MOUSE_BUTTON_RIGHT):
			_show_crack(event.position)

func _show_crack(_pos: Vector2):
	var screen_pos = get_viewport().get_mouse_position()
	
	# Reset timer
	crack_timer = crack_duration
	is_cracked = true
	
	# Play crack sound if assigned
	if crack_sound:
		crack_sound.play()
	
	# Update shader parameters
	material.set_shader_parameter("click_position", screen_pos)
	material.set_shader_parameter("show_crack", true)

func _process(delta):
	if is_cracked:
		crack_timer -= delta
		
		# Remove crack when timer expires
		if crack_timer <= 0.0:
			_set_crack_visible(false)

## Public API: Manually trigger a crack
func trigger_crack():
	_show_crack(Vector2.ZERO)

## Public API: Check if glass is currently cracked
func is_cracked_state() -> bool:
	return is_cracked

Optional Features:

  • Optional: Add an AudioStreamPlayer child and assign it to crack_sound

  • Adjust parameters in the inspector to control crack duration, complexity, and visual style

  • That’s it! Click anywhere and watch the glass crack.
Shader code
shader_type canvas_item;

uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, filter_linear_mipmap;

uniform vec2 click_position = vec2(0.0, 0.0);
uniform bool show_crack = false;
uniform float crack_thickness = 0.25;
uniform int point_count = 30;
uniform float crack_intensity = 1.0;

uniform float refraction_strength = 5.0;
uniform float blur_amount = 2.0;
uniform float glass_tint = 0.1;
uniform float crack_glow = 0.5;

uniform vec4 crack_color : source_color = vec4(0.118, 0.118, 0.118, 0.588);
uniform float heal_amount = 0.0;

// random
float random(vec2 st) {
    return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123);
}

// noise
float noise(vec2 st) {
    vec2 i = floor(st);
    vec2 f = fract(st);

    float a = random(i);
    float b = random(i + vec2(1.0, 0.0));
    float c = random(i + vec2(0.0, 1.0));
    float d = random(i + vec2(1.0, 1.0));

    vec2 u = f * f * (3.0 - 2.0 * f);
    return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
}

// voronoi edge
float voronoi_edge(vec2 uv, int count, out float min_dist, out float second_dist, out vec2 nearest_point) {
    min_dist = 1000.0;
    second_dist = 1000.0;
    nearest_point = uv;

    for (int i = 0; i < 50; i++) {
        if (i >= count) break;

        float t = float(i) / float(count - 1);
        float max_radius = 500.0;
        float r = max_radius * pow(t, 1.2);

        float angle = random(vec2(float(i), click_position.x + click_position.y)) * 6.28318;
        angle += noise(vec2(float(i) * 0.1, 0.0)) * 1.0;

        vec2 point = click_position + vec2(cos(angle), sin(angle)) * r;

        float dist = distance(uv, point);

        if (dist < min_dist) {
            second_dist = min_dist;
            min_dist = dist;
            nearest_point = point;
        } else if (dist < second_dist) {
            second_dist = dist;
        }
    }

    return second_dist - min_dist;
}

void fragment() {
    vec2 uv = FRAGCOORD.xy;
    vec2 screen_uv = SCREEN_UV;
    
    // --- BASE GLASS EFFECT (applies to entire glass surface) ---
    // Add subtle constant refraction to whole glass
    vec2 base_refraction = vec2(
        noise(uv * 0.01) * 0.0005,
        noise(uv * 0.01 + 100.0) * 0.0005
    );
    
    vec2 glass_uv = screen_uv + base_refraction;
    vec4 glass_color = texture(SCREEN_TEXTURE, glass_uv);
    
    // Add subtle blur to entire glass
    vec4 blur_col = vec4(0.0);
    float total = 0.0;
    int samples = 1; // Light blur on whole glass
    for (int x = -samples; x <= samples; x++) {
        for (int y = -samples; y <= samples; y++) {
            vec2 offset = vec2(float(x), float(y)) * 0.0005;
            blur_col += texture(SCREEN_TEXTURE, glass_uv + offset);
            total += 1.0;
        }
    }
    blur_col /= total;
    
    // Apply glass tint to entire surface
    glass_color = mix(blur_col, vec4(0.8, 0.9, 1.0, 1.0), glass_tint * 0.5);
    
    // --- CRACK EFFECT (only if showing) ---
    if (show_crack) {
        float min_dist, second_dist;
        vec2 nearest_point;

        float edge_value = voronoi_edge(uv, point_count, min_dist, second_dist, nearest_point);

        float is_edge = 1.0 - smoothstep(0.0, crack_thickness, edge_value);

        float crack_noise = noise(uv * 0.05) * 0.2;
        is_edge = clamp(is_edge + crack_noise * 0.1, 0.0, 1.0);

        // Apply healing
        float heal_factor = 1.0 - heal_amount;
        is_edge = is_edge * heal_factor;
        
        float dissolve = smoothstep(heal_amount - 0.2, heal_amount + 0.3, is_edge);
        
        vec2 shard_dir = normalize(uv - nearest_point);
        float shard_strength = 1.0 - smoothstep(0.0, 250.0, min_dist);
        
        // Enhanced refraction near cracks
        float current_refraction = refraction_strength * (1.0 - heal_amount * 0.8);

        vec2 magnify = (uv - nearest_point) * 0.0008 * shard_strength;

        vec2 refracted_uv = screen_uv
            + shard_dir * current_refraction * 0.001 * shard_strength
            + magnify;

        // Stronger blur near cracks
        vec4 crack_blur = vec4(0.0);
        float crack_total = 0.0;
        int crack_samples = int(blur_amount);
        for (int x = -crack_samples; x <= crack_samples; x++) {
            for (int y = -crack_samples; y <= crack_samples; y++) {
                vec2 offset = vec2(float(x), float(y)) * 0.001;
                crack_blur += texture(SCREEN_TEXTURE, refracted_uv + offset);
                crack_total += 1.0;
            }
        }
        crack_blur /= crack_total;
        crack_blur = mix(crack_blur, vec4(0.8, 0.9, 1.0, 1.0), glass_tint);

        // Mix between base glass and crack effect based on distance to crack
        float mix_factor = smoothstep(0.0, 150.0, min_dist);
        mix_factor = 1.0 - mix_factor;
        mix_factor = mix_factor * (1.0 - heal_amount);
        
        glass_color = mix(glass_color, crack_blur, mix_factor);

        // Crack glow
        float glow = is_edge * crack_glow * crack_intensity * (1.0 - heal_amount);
        float dist_center = distance(uv, click_position);
        float inner_glow = (1.0 - smoothstep(0.0, 80.0, dist_center)) * 0.3 * (1.0 - heal_amount);
        float crack_light = glow + inner_glow;

        glass_color.rgb += crack_color.rgb * crack_light;
        
        if (dissolve < 0.1) {
            // Keep base glass effect even when crack is healing
            glass_color = mix(glass_color, glass_color, 0.0);
        }
    }

    COLOR = vec4(glass_color.rgb, 1.0);
}
Live Preview
Tags
broken glass, crack, cracked glass, glass, glass shader, Main Menu, projektsansstudios, Shards, Shatter, shatter ui, ui, voronoi
The shader code and all code snippets in this post are under MIT license and can be used freely. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from Lord0Sanz

Related shaders

guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments