shader_type canvas_item; uniform sampler2D screen_texture : hint_screen_texture, filter_linear_mipmap; // --- 1. COLORE E VISIBILITÀ --- // Ho alzato un po' l'Alpha base per renderli più visibili fin da subito uniform vec4 dust_color : source_color = vec4(1.0, 0.9, 0.7, 0.6); // --- 2. VENTO E MOVIMENTO --- // X negativo = verso sinistra. Y positivo = verso il basso. uniform vec2 wind_velocity = vec2(-0.2, 0.05); uniform float wind_oscillation : hint_range(0.0, 1.0) = 0.3; uniform float dust_speed : hint_range(0.01, 1.0) = 0.15; // --- 3. DIMENSIONI E DENSITÀ --- uniform float size_min : hint_range(0.001, 0.1) = 0.02; uniform float size_max : hint_range(0.01, 0.5) = 0.2; uniform float dust_density : hint_range(0.01, 10.0) = 1.0; // --- 4. IL FIX DEL CENTRO (Morbidezza) --- // 0.0 = Sfumatura totale dal centro esatto fino al bordo. // 0.5 = Metà pallino è solido, l'altra metà sfuma. // 0.9 = Pallino dai bordi durissimi. uniform float solid_core : hint_range(0.0, 0.99) = 0.1; vec3 hash33(vec3 p3) { p3 = fract(p3 * vec3(0.1031, 0.1030, 0.0973)); p3 += dot(p3, p3.yzx + 33.33); return fract((p3.xxy + p3.yzz) * p3.zyx); } void fragment() { vec4 scene_color = texture(screen_texture, SCREEN_UV); float base_time = TIME * dust_speed; // Vento con oscillazione naturale vec2 osc = vec2(sin(TIME * 0.5), cos(TIME * 0.3)) * wind_oscillation; vec2 final_wind = (wind_velocity + osc) * dust_speed; float final_particle_alpha = 0.0; // Configurazione dei 3 strati (Vicino, Medio, Lontano) vec3 layer_scales = vec3(20.0, 50.0, 100.0) / dust_density; vec3 layer_thresholds = vec3(0.96, 0.98, 0.99); vec3 layer_speeds = vec3(1.0, 0.6, 0.3); for (int i = 0; i < 3; i++) { float scale = layer_scales[i]; float threshold = layer_thresholds[i]; float speed_mult = layer_speeds[i]; // Spostiamo la griglia con il vento vec2 st = (SCREEN_UV * scale) - (final_wind * speed_mult * base_time * 50.0); vec2 ipos = floor(st); vec2 fpos = fract(st); vec3 rand = hash33(vec3(ipos, float(i))); if (rand.z > threshold) { // Effetto brillio float blink = sin(TIME * (2.0 + rand.x)) * 0.5 + 0.5; float d = length(fpos - vec2(0.5)); float size = size_min + (size_max - size_min) * rand.x; float scaled_size = size * (0.5 + 0.5 * blink); // --- LA SOLUZIONE "ANTI-CIAMBELLA" --- // Calcoliamo la grandezza del "nucleo solido" in proporzione alla grandezza totale. float core = scaled_size * solid_core; // Se la distanza (d) è minore del 'core', l'opacità è 1.0. // Se va dal 'core' fino al bordo (scaled_size), sfuma dolcemente verso lo 0.0. float particle_softness = 1.0 - smoothstep(core, scaled_size, d); // Uniamo tutto: morbidezza, brillio, e una correzione per l'intensità in base allo strato float intensity = (rand.z - threshold) / (1.0 - threshold); final_particle_alpha += particle_softness * blink * intensity; } } final_particle_alpha = clamp(final_particle_alpha, 0.0, 1.0); vec4 dust_colored_particle = dust_color * final_particle_alpha; COLOR = scene_color + dust_colored_particle; }