shader_type spatial; render_mode unshaded, cull_disabled; uniform float movement_Factor : hint_range(6.0,36.0) = 6.0; uniform float rotation_speed : hint_range(-5.0, 5.0) = 1.0; uniform float scroll_speed : hint_range(-5.0, 5.0) = 0.5; uniform float vertex_displacement : hint_range(0.0, 5.0) = 0.2; uniform float inner_fade = 0.2; uniform float outer_fade = 0.8; uniform vec4 color : source_color = vec4(1.0); uniform vec4 spiral_color : source_color = vec4(0.8, 0.8, 0.8, 1.0); uniform float spiral_turns : hint_range(1.0, 48.0) = 1.0; uniform float spiral_swirl : hint_range(6.0, 360.0) = 6.0; uniform float spiral_width : hint_range(0.02, 0.5) = 0.047; uniform float spiral_strands : hint_range(1.0, 2.0) = 1.0; uniform sampler2D noise_tex : source_color; uniform sampler2D cloud_tex : source_color; // ── Shared helpers ────────────────────────────────────────────────────── // Rotate UV.x around the cylinder and scroll UV.y over time. vec2 rotate_and_scroll(vec2 uv, float t) { float angle = uv.x * (2.0 * PI); angle += t * rotation_speed; uv.x = angle / (2.0 * PI); uv.y += t * scroll_speed; return uv; } // Noise UV used for both distortion and displacement. vec2 noise_uv(vec2 uv, float t) { return uv * 3.0 + vec2(t * 0.2, t * 0.15); } // Sample noise and apply distortion offset to the rotated UVs. vec2 distort_uv(vec2 rotated_uv, vec2 raw_uv, float t, float distortion_amount) { float n = texture(noise_tex, noise_uv(raw_uv, t)).r; return rotated_uv + (n - 0.5) * distortion_amount; } // Full cloud colour at a given UV, including noise distortion. vec4 sample_cloud(vec2 uv, float t) { float distortion_amount = (sin(t * 0.1) * 0.5 + 0.5) / movement_Factor; vec2 ruv = rotate_and_scroll(uv, t); vec2 final_uv = distort_uv(ruv, uv, t, distortion_amount); return texture(cloud_tex, final_uv); } // Spiral mask in tunnel UV space. Uses the same motion params as the clouds. // `spiral_strands` lets you blend from a single helix (1) to dual helix (2). float spiral_mask(vec2 uv, float t) { vec2 moved_uv = rotate_and_scroll(uv, t); float u = fract(moved_uv.x); float v = fract(moved_uv.y); // Primary helix. float helix_a = fract(u * spiral_turns - v * spiral_swirl); float dist_a = abs(helix_a - 0.5); float half_w = spiral_width * 0.5; float thread_a = 1.0 - smoothstep(half_w, half_w + 0.06, dist_a); // Optional opposing helix for barber-pole look. float helix_b = fract(u * spiral_turns + v * spiral_swirl + 0.5); float dist_b = abs(helix_b - 0.5); float thread_b = 1.0 - smoothstep(half_w, half_w + 0.06, dist_b); float use_second = clamp(spiral_strands - 1.0, 0.0, 1.0); return max(thread_a, thread_b * use_second); } vec3 apply_spiral_color(vec2 uv, float t, vec3 base_rgb) { float mask = spiral_mask(uv, t) * spiral_color.a; return mix(base_rgb, spiral_color.rgb, mask); } // ── Vertex & Fragment ─────────────────────────────────────────────────── void vertex() { float t = TIME + 30.0; // Sample the full cloud pattern — same logic as fragment() vec4 cloud = sample_cloud(UV, t); // Also grab raw noise for high-frequency displacement float n = texture(noise_tex, noise_uv(UV, t)).r; // Combine cloud luminance with noise for displacement float luminance = dot(cloud.rgb, vec3(0.299, 0.587, 0.114)); float disp = ((luminance + n) * 0.5 - 0.5) * vertex_displacement; VERTEX += NORMAL * disp; } void fragment() { float t = TIME + 30.0; // Sample cloud using the shared pipeline vec4 col = sample_cloud(UV, t); // Radial fade (soft edges) float radius = length(UV - vec2(0.5)); float fade = smoothstep(inner_fade, outer_fade, radius); col.rgb *= color.rgb; col.rgb = apply_spiral_color(UV, t, col.rgb); col.a *= fade; ALBEDO = col.rgb; ALPHA = col.a; }