Created
October 11, 2025 04:16
-
-
Save ChrisPritchard/81a994b5a909fa425f5e975a3349ec6d to your computer and use it in GitHub Desktop.
A godot shader that generates a moving circuit like pattern.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| shader_type canvas_item; | |
| uniform float aspect_ratio: hint_range(0.1, 1.9, 0.1) = 1.0; | |
| group_uniforms line_characteristics; | |
| uniform float line_width: hint_range(0.001, 0.1) = 0.01; | |
| uniform int segment_count: hint_range(0, 10, 1) = 5; | |
| uniform float segment_length: hint_range(0.1, 1.0, 0.01) = 0.2; | |
| uniform float tail_lag: hint_range(0.1, 1.0, 0.01) = 0.4; | |
| uniform vec4 line_colour: source_color = vec4(1.0, 1.0, 0, 1.0); | |
| uniform float head_size_multiplier: hint_range(0.0, 10.0, 0.1) = 5.0; | |
| group_uniforms spawn_characteristics; | |
| uniform int line_count: hint_range(0, 200, 1) = 10; | |
| uniform float line_lifetime: hint_range(0.5, 10.0) = 5.0; | |
| uniform vec2 top_left_margin = vec2(0.2, 0.2); | |
| uniform vec2 bottom_right_margin = vec2(0.8, 0.8); | |
| float rand(int seed) { | |
| float fseed = float(seed); | |
| return fract(sin(dot(vec2(fseed, fseed * 0.66), vec2(12.9898, 78.233))) * 43758.5453); | |
| } | |
| float get_start(int seed) { | |
| float rand_dir = floor(rand(seed) * 4.0); | |
| return rand_dir * radians(90); | |
| } | |
| float get_turn(int seed, float initial, float angle_size) { | |
| int rand_dir = int(floor(rand(seed) * 2.0)); | |
| if(rand_dir == 0) | |
| return initial + angle_size; | |
| else | |
| return initial - angle_size; | |
| } | |
| float get_segment_intensity( | |
| vec2 start, vec2 end, | |
| vec2 segment_process_range, | |
| float progress, | |
| vec2 uv) { | |
| vec2 segment_vec = end - start; | |
| vec2 pixel_vec_segment = uv - start; | |
| float t = clamp(dot(pixel_vec_segment, segment_vec) / (segment_length * segment_length), 0.0, 1.0); | |
| vec2 closest_point = start + t * segment_vec; | |
| float distance_to_segment = distance(uv, closest_point); | |
| float point_progress = mix(segment_process_range.x, segment_process_range.y, t); | |
| if (point_progress <= min(progress, 1.0 - tail_lag) && point_progress >= progress - tail_lag) { | |
| float intensity = 1.0 - smoothstep(0.0, line_width, distance_to_segment); | |
| float lit_progress = (point_progress - (progress - tail_lag)) / tail_lag; | |
| return intensity * lit_progress; | |
| } | |
| return 0.0; | |
| } | |
| float render_line(float time, vec2 uv) { | |
| float local_time = time / line_lifetime; | |
| int seed = int(floor(local_time)); | |
| float progress = fract(local_time); | |
| float line_intensity = 0.0; | |
| vec2 last_point = vec2(rand(seed), rand(seed + 1)); | |
| last_point = top_left_margin + last_point * (bottom_right_margin - top_left_margin); | |
| float last_angle = get_start(seed); | |
| vec2 last_dir = vec2(cos(last_angle), sin(last_angle)); | |
| float last_progress = 0.0; | |
| for(int j = 0; j < segment_count; j++) { | |
| vec2 segment_bounds = vec2(last_progress, last_progress + (1.0-tail_lag)/float(segment_count)); | |
| last_progress = segment_bounds.y; | |
| if(progress < segment_bounds.x) | |
| break; | |
| vec2 next_point = last_point + last_dir * segment_length; | |
| float next_angle = get_turn(seed + j, last_angle, radians(45)); | |
| vec2 next_dir = vec2(cos(next_angle), sin(next_angle)); | |
| line_intensity = max(line_intensity, get_segment_intensity( | |
| last_point, next_point, | |
| segment_bounds, | |
| progress, | |
| uv)); | |
| if(progress >= segment_bounds.x && progress <= segment_bounds.y) { | |
| vec2 head_pos = last_point + last_dir * segment_length * (progress - segment_bounds.x) / (segment_bounds.y - segment_bounds.x);; | |
| float head_glow = 1.0 - smoothstep(0.0, line_width * head_size_multiplier, distance(uv, head_pos)); | |
| line_intensity = max(line_intensity, head_glow); | |
| } | |
| last_point = next_point; | |
| last_angle = next_angle; | |
| last_dir = next_dir; | |
| } | |
| return line_intensity; | |
| } | |
| void fragment() { | |
| vec4 under_colour = COLOR; | |
| vec2 centered = UV - vec2(0.5); | |
| centered.x *= aspect_ratio; | |
| vec2 uv = centered + vec2(0.5); | |
| float line_intensity = 0.0; | |
| for(int i = 0; i < line_count; i++) { | |
| float offset = float(i) * 123.123; | |
| line_intensity = max(line_intensity, render_line(TIME + offset, uv)); | |
| } | |
| COLOR = mix(under_colour, line_colour, line_intensity * line_colour.a); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment