Fairy Lights

của MusicBar, thanh tiến trình

Dây đèn nháy kiểu Giáng sinh sáng tới chỗ đang phát, bóng nào cũng lấp lánh, tới nhịp là một vệt sáng chạy dọc dây.

7 lượt tải Tải file, 4.7 KB
Cách cài, mã SHA-256 và mã nguồn

Bấm Áp dụng: MusicBar tự tải về, kiểm tra mã SHA-256 rồi mặc lên taskbar; có rồi thì mặc luôn. Cần MusicBar 1.8 trở lên.

SHA-256 5a7f67c1761b6291374079361812f8e7a46457eac714d4106332972df7868b58

//! name: Fairy Lights
//! author: MusicBar
//! kind: progress
//! description: A twisted wire of glass fairy lights swags along the bar, lit up to the playhead and twinkling, with a chase racing down the string on every beat.

float hash1(float n)
{
    n = frac(n * 0.1031);
    n *= n + 33.33;
    n *= n + n;
    return frac(n);
}

// Lays a color over what is there (premultiplied light, and how much of the pixel it covers).
void layer(inout float3 acc, inout float cover, float3 rgb, float a)
{
    a = saturate(a);
    acc = rgb * a + acc * (1.0 - a);
    cover = a + cover * (1.0 - a);
}

float2 rot(float2 p, float a)
{
    float c = cos(a), s = sin(a);
    return float2(c * p.x - s * p.y, s * p.x + c * p.y);
}

float boxD(float2 p, float2 b, float r)
{
    float2 d = abs(p) - b + r;
    return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0) - r;
}

// Bulb colors run through the palette.
float3 tint(float i)
{
    float k = frac(i / 3.0) * 3.0;
    return k < 0.5 ? colorA.rgb : (k < 1.5 ? colorB.rgb : colorC.rgb);
}

// The wire sags in swags between hooks every P pixels; returns its height and slope.
float2 wire(float x, float P, float H)
{
    float f = frac(x / P);
    return float2(H * 0.92 - H * 1.36 * f * (1.0 - f), -H * 1.36 * (1.0 - 2.0 * f) / P);
}

// Bulb i: a plastic socket on the wire and a glass bulb hanging under it.
void bulb(inout float3 acc, inout float cover, float2 px, float i, float sp, float P, float H, float head)
{
    float xi = (i + 0.5) * sp;
    float2 w = wire(xi, P, H);
    float h = hash1(i * 3.17 + 0.5);
    float ang = atan(w.y) * 0.7 + (h - 0.5) * 0.6 + 0.05 * sin(time * 1.4 + i);
    float2 q = rot(px - float2(xi, w.x), -ang);

    // Lit behind the playhead (the newest one stutters on), twinkling, and a chase on the beat.
    float on = saturate((head - xi) / sp + 0.5);
    on *= lerp(0.45 + 0.55 * abs(sin(time * 29.0 + i) * sin(time * 43.0)), 1.0, step(0.999, on));
    float tw = 0.75 + 0.25 * sin(time * (2.0 + 3.0 * h) + h * 40.0);
    float chase = smoothstep(0.7, 1.0, frac(i * 0.25 - time * 1.7));
    float glow = on * tw * (1.0 + (0.2 + 1.8 * beat) * chase + 0.3 * beat) * (0.85 + 0.25 * level);
    float3 c = tint(i);

    // The glass: an egg that narrows to a tip.
    float2 p = q - float2(0.0, -0.27 * H);
    float2 r = float2(0.12, 0.17) * H;
    float taper = 1.0 + 0.5 * saturate(-p.y / r.y);
    float e = length(float2(p.x * taper, p.y) / r);
    float sd = (e - 1.0) * r.x;

    // Halo first, then the glass with its hot middle and a window-shaped highlight.
    float hd = length(p * float2(1.0, 0.8)) / H;
    layer(acc, cover, lerp(c, float3(1, 1, 1), 0.1), (exp(-hd * hd * 22.0) * 0.5 + exp(-hd * hd * 70.0) * 0.3) * glow);
    float core = exp(-dot(p / r, p / r) * 2.0);
    float3 litC = c * (0.55 + 0.7 * core) * glow + core * core * 0.75 * glow;
    float3 darkC = c * (0.08 + 0.32 * smoothstep(0.65, 1.0, e)) + 0.02;
    float3 glass = lerp(darkC, litC, saturate(on));
    float spec = smoothstep(1.0, 0.3, length((p - float2(-0.045, 0.05) * H) / (float2(0.022, 0.055) * H)));
    glass += spec * (0.4 + 0.3 * on);
    layer(acc, cover, glass, smoothstep(0.5, -0.5, sd) * lerp(0.85, 1.0, on));

    // The socket, catching a little of its bulb's light.
    float sk = boxD(q - float2(0.0, -0.065 * H), float2(0.055, 0.065) * H, 0.02 * H);
    float3 skC = float3(0.05, 0.1, 0.06) + c * 0.25 * on + 0.18 * smoothstep(0.03 * H, -0.04 * H, q.x);
    layer(acc, cover, skC, smoothstep(0.5, -0.5, sk));
}

float4 main(float2 uv : TEXCOORD) : COLOR
{
    // Pixels, y up the screen, so the bulbs always hang down.
    float H = size.y;
    float2 px = float2(uv.x * size.x, (1.0 - uv.y) * H);
    float sp = 0.78 * H;
    float P = sp * 4.0;
    float head = progress * size.x;
    float played = smoothstep(head + 0.4 * H, head - 0.4 * H, px.x);

    float3 acc = float3(0, 0, 0);
    float cover = 0;

    // The wire: two strands twisted round each other, warmed by the lit bulbs.
    float2 w = wire(px.x, P, H);
    float n = sqrt(1.0 + w.y * w.y);
    float tw = sin(px.x * 6.2832 / (0.16 * H));
    float th = max(0.5, 0.014 * H);
    float s1 = abs(px.y - w.x - 0.02 * H * tw) / n;
    float s2 = abs(px.y - w.x + 0.02 * H * tw) / n;
    float3 wc = lerp(float3(0.07, 0.13, 0.08), float3(0.3, 0.26, 0.17), played);
    layer(acc, cover, wc * 0.7, smoothstep(th + 0.5, th - 0.5, s2));
    layer(acc, cover, wc * (1.0 + 0.6 * step(0.0, tw)), smoothstep(th + 0.5, th - 0.5, s1));

    // The two nearest bulbs: the neighbor first, then this one on top.
    float i = floor(px.x / sp);
    float j = i + (frac(px.x / sp) > 0.5 ? 1.0 : -1.0);
    bulb(acc, cover, px, j, sp, P, H, head);
    bulb(acc, cover, px, i, sp, P, H, head);

    return paint(acc / max(cover, 0.001), cover);
}