131 lines
5.3 KiB
Plaintext
131 lines
5.3 KiB
Plaintext
shader_type canvas_item;
|
|
// Tendone del menu: chase delle lucine delle garlande, attivo solo in hover.
|
|
// Il chase replica l'animazione sorgente (animazione_tendone.gif, 12,5 fps):
|
|
// periodo 6 frame, 3 fasi da 2 frame; ogni lampadina fa rampa per 2 frame,
|
|
// brilla a pieno intensità per 2 frame e resta spenta per 2 frame.
|
|
// Lato sinistro: l'onda sale dal fondo verso il picco (fasi [4,2,0] per i mod 3).
|
|
// Lato destro: l'onda scende dal picco al fondo (fasi [0,2,4] per i mod 3);
|
|
// al picco L0 (fase 0) precede R0 (fase 1) di 2 frame, come nella GIF.
|
|
|
|
uniform float hover_strength : hint_range(0.0, 1.0) = 0.0;
|
|
uniform vec2 star_center = vec2(0.50, 0.13);
|
|
uniform float drift_strength : hint_range(0.0, 4.0) = 0.65;
|
|
uniform float drift_speed : hint_range(0.0, 2.0) = 0.20;
|
|
// Intensità del chase lucine: 0 = spento a riposo, 1.0 = attivo in hover.
|
|
uniform float garland_strength : hint_range(0.0, 1.0) = 0.0;
|
|
// FPS sorgente dell'animazione (GIF a 8 centesimi di secondo per frame).
|
|
uniform float garland_fps : hint_range(1.0, 60.0) = 12.5;
|
|
// Frame assoluto con cui congelare il chase (debug/catture); -1 = tempo reale.
|
|
uniform float garland_freeze_frame = -1.0;
|
|
|
|
// Nucleo della lampadina accesa e colore dell'alone, campionati dalla GIF.
|
|
const vec3 GOLD_CORE = vec3(0.945, 0.80, 0.385);
|
|
const vec3 GOLD_HALO = vec3(1.0, 0.78, 0.35);
|
|
|
|
// Posizioni lampadina in UV texture (canvas 880x750) + offset di fase in frame
|
|
// nel ciclo di 6: z = frame di inizio rampa. L0..L9 dal picco al fondo sinistro,
|
|
// R0..R9 dal picco al fondo destro.
|
|
const vec3 bulbs[20] = vec3[20](
|
|
// garlanda sinistra: onda che sale
|
|
vec3(0.4500, 0.2893, 4.0), vec3(0.4330, 0.3187, 2.0), vec3(0.4125, 0.3453, 0.0),
|
|
vec3(0.3875, 0.3747, 4.0), vec3(0.3591, 0.4080, 2.0), vec3(0.3341, 0.4360, 0.0),
|
|
vec3(0.3000, 0.4693, 4.0), vec3(0.2670, 0.5013, 2.0), vec3(0.2295, 0.5307, 0.0),
|
|
vec3(0.1920, 0.5587, 4.0),
|
|
// garlanda destra: onda che scende
|
|
vec3(0.5307, 0.3000, 0.0), vec3(0.5443, 0.3333, 2.0), vec3(0.5648, 0.3573, 4.0),
|
|
vec3(0.5852, 0.3840, 0.0), vec3(0.6205, 0.4253, 2.0), vec3(0.6420, 0.4573, 4.0),
|
|
vec3(0.6761, 0.4880, 0.0), vec3(0.7114, 0.5160, 2.0), vec3(0.7466, 0.5427, 4.0),
|
|
vec3(0.7773, 0.5613, 0.0)
|
|
);
|
|
|
|
// 4 grandi luci sui punti di ancoraggio delle funi a terra lungo la balza:
|
|
// 0: Esterno Sinistro (131.3, 502.1) -> fase 4 (in fase con L9 in fondo a sinistra)
|
|
// 1: Palo/Fune SX Ingresso (306.5, 544.1) -> fase 0
|
|
// 2: Palo/Fune DX Ingresso (558.6, 550.7) -> fase 2
|
|
// 3: Esterno Destro (737.2, 499.3) -> fase 4 (in fase con la discesa a destra)
|
|
const vec3 anchor_bulbs[4] = vec3[4](
|
|
vec3(0.1492, 0.6695, 4.0),
|
|
vec3(0.3483, 0.7255, 0.0),
|
|
vec3(0.6348, 0.7342, 2.0),
|
|
vec3(0.8378, 0.6657, 4.0)
|
|
);
|
|
|
|
// Intensità nel ciclo di 6 frame della singola lampadina (d = frame dalla rampa),
|
|
// ricavata dalle aree misurate sui frame della GIF: ~25%, ~50%, pieno, pieno, 0, 0.
|
|
float chase_intensity(float d) {
|
|
if (d < 1.0) {
|
|
return 0.3;
|
|
}
|
|
if (d < 2.0) {
|
|
return 0.6;
|
|
}
|
|
if (d < 3.0) {
|
|
return 1.0;
|
|
}
|
|
if (d < 4.0) {
|
|
return 0.95;
|
|
}
|
|
return 0.0;
|
|
}
|
|
|
|
void vertex() {
|
|
float wave = sin(TIME * drift_speed + VERTEX.y * 0.012) * drift_strength;
|
|
VERTEX.x += wave * (UV.y * 0.7 + 0.3);
|
|
}
|
|
|
|
void fragment() {
|
|
vec4 color = texture(TEXTURE, UV);
|
|
// Maschera morbida limitata alla stella in cima al tendone, non al tendone.
|
|
vec2 aspect_uv = (UV - star_center) * vec2(1.0, 1.35);
|
|
float star_area = 1.0 - smoothstep(0.105, 0.185, length(aspect_uv));
|
|
float pulse = 0.88 + 0.12 * sin(TIME * 5.0);
|
|
vec3 gold_glow = vec3(1.0, 0.70, 0.14) * star_area * hover_strength * pulse;
|
|
color.rgb += gold_glow * color.a;
|
|
|
|
// Chase lucine: sincronizzato a 12,5 fps come la GIF sorgente.
|
|
if (garland_strength > 0.001) {
|
|
float frame_time = TIME * garland_fps;
|
|
if (garland_freeze_frame >= 0.0) {
|
|
frame_time = garland_freeze_frame;
|
|
}
|
|
float cycle = mod(floor(frame_time), 6.0);
|
|
vec2 px = UV * vec2(880.0, 750.0);
|
|
for (int i = 0; i < 20; i++) {
|
|
float d = mod(cycle - bulbs[i].z + 6.0, 6.0);
|
|
float intensity = chase_intensity(d);
|
|
if (intensity <= 0.0) {
|
|
continue;
|
|
}
|
|
float r = length(px - bulbs[i].xy * vec2(880.0, 750.0));
|
|
// Nucleo pieno ~6px + alone ampio e diffuso fino a ~25px (come nella GIF).
|
|
float core = 1.0 - smoothstep(3.0, 8.0, r);
|
|
float halo = exp(-r / 12.0);
|
|
float ga = clamp(intensity * garland_strength, 0.0, 1.0);
|
|
// Il nucleo porta la lampadina all'oro pieno della GIF.
|
|
color.rgb = mix(color.rgb, GOLD_CORE, core * ga);
|
|
// Alone caldo additivo solo attorno al nucleo (senza sforare sul nucleo,
|
|
// altrimenti il clamp sbiadisce l'oro verso il bianco).
|
|
color.rgb += GOLD_HALO * (halo * ga * 0.7 * (1.0 - core));
|
|
color.a = max(color.a, halo * ga * 0.5);
|
|
}
|
|
|
|
// 4 grandi luci sui punti di ancoraggio delle funi (intermittenti a inseguimento con ritmo proprio)
|
|
for (int j = 0; j < 4; j++) {
|
|
float d = mod(cycle - anchor_bulbs[j].z + 6.0, 6.0);
|
|
float intensity = chase_intensity(d);
|
|
if (intensity <= 0.0) {
|
|
continue;
|
|
}
|
|
float r = length(px - anchor_bulbs[j].xy * vec2(880.0, 750.0));
|
|
float core = 1.0 - smoothstep(4.0, 10.0, r);
|
|
float halo = exp(-r / 15.0);
|
|
float ga = clamp(intensity * garland_strength, 0.0, 1.0);
|
|
color.rgb = mix(color.rgb, GOLD_CORE, core * ga);
|
|
color.rgb += GOLD_HALO * (halo * ga * 0.8 * (1.0 - core));
|
|
color.a = max(color.a, halo * ga * 0.6);
|
|
}
|
|
}
|
|
|
|
color.rgb = min(color.rgb, vec3(1.0));
|
|
COLOR = color;
|
|
} |