Hội An Lanterns

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

Dây đèn lồng Hội An sáng dần tới chỗ đang phát, chiếc nào cũng đung đưa trong gió đêm, tới nhịp là lắc mạnh hơn.

10 lượt tải Tải file, 6.0 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 1fec7795e1a6371df6fd6f05ee40daba3c680ea917e3aa1cd25219dfc27cfb1f

//! name: Hội An Lanterns
//! author: MusicBar
//! kind: progress
//! description: A string of Hoi An silk lanterns glows to life up to the playhead, swaying in the night breeze and swinging harder 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);
}

// Signed distance to a rounded box (half size b, corner r).
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;
}

// The silk of lantern i cycles through the palette.
float3 silkOf(float i)
{
    float k = frac(i / 3.0) * 3.0;
    return k < 0.5 ? colorA.rgb : (k < 1.5 ? colorB.rgb : colorC.rgb);
}

// Lantern i hangs from the cord: thread, top cap, ribbed silk body, bottom cap and tassel.
void lantern(inout float3 acc, inout float cover, float2 px, float i, float sp, float H, float head)
{
    float cx = (i + 0.5) * sp;
    float a = (0.05 + 0.07 * level) * sin(time * 1.2 + i * 1.7) + 0.16 * beat * sin(i * 2.3 + 1.0);
    float2 q = rot(px - float2(cx, H * 0.96), -a);

    // How lit it is: on behind the playhead, flickering while it catches.
    float on = saturate((head - cx) / sp + 0.5);
    on *= lerp(0.55 + 0.45 * sin(time * 23.0 + i) * sin(time * 37.0), 1.0, step(0.999, on));
    float3 silk = silkOf(i);
    float bright = 1.0 + 0.3 * beat + 0.12 * level;

    // Body: an ellipse with flat ends where the caps sit.
    float2 p = q - float2(0.0, -0.405 * H);
    float2 r = float2(0.30, 0.27) * H;
    float e = length(p / r);
    float sd = max((e - 1.0) * r.y, abs(p.y) - 0.235 * H);

    // The warm halo the silk throws around itself.
    layer(acc, cover, lerp(silk, float3(1.0, 0.86, 0.6), 0.35), exp(-max(sd, 0.0) / (0.2 * H)) * 0.5 * on * bright);

    // Ribs run pole to pole; the silk bulges between them and glows brightest in the middle.
    float R = r.x * sqrt(saturate(1.0 - p.y * p.y / (r.y * r.y)));
    float phi = asin(clamp(p.x / max(R, 0.001), -1.0, 1.0));
    float s = phi * 2.546 + time * 0.12 + i * 0.37;
    float dd = 0.5 - abs(frac(s) - 0.5);
    float rib = smoothstep(0.9, 0.15, dd * 0.3927 * R * cos(phi));
    float bulge = sin(dd * 3.14159);
    float core = exp(-dot(p / r, p / r) * 1.7);
    float3 lit = silk * (0.5 + 0.8 * core) * (0.7 + 0.3 * bulge) + float3(1.0, 0.92, 0.75) * core * core * core * 0.6;
    float3 dark = silk * (0.1 + 0.28 * smoothstep(0.75, 1.0, e)) + 0.03;
    float trim = smoothstep(1.2, 0.3, abs(abs(p.y) - 0.2 * H));
    float3 body = lerp(dark, lit * bright, on);
    body *= 1.0 - (0.6 * rib + 0.35 * trim) * (0.35 + 0.65 * on);
    body += float3(1.0, 0.8, 0.4) * trim * (0.08 + 0.3 * on);
    layer(acc, cover, body, smoothstep(0.5, -0.5, sd) * lerp(0.85, 1.0, on));

    // Thread and lacquered caps, warmed by the light inside.
    float3 wood = float3(0.2, 0.07, 0.035) + silk * 0.2 * on;
    float thread = abs(q.x) - 0.5;
    layer(acc, cover, wood, smoothstep(0.5, -0.5, thread) * step(-0.12 * H, q.y) * step(q.y, 0.0));
    float capT = boxD(q - float2(0.0, -0.15 * H), float2(0.12, 0.035) * H, 0.025 * H);
    float capB = boxD(q - float2(0.0, -0.665 * H), float2(0.1, 0.03) * H, 0.025 * H);
    float capD = min(capT, capB);
    float edge = smoothstep(1.0, 0.0, abs(capD + 0.6));
    float band = smoothstep(0.6, 0.0, abs(frac(q.x / (0.06 * H)) - 0.5) - 0.3);
    float3 capC = wood + float3(0.75, 0.48, 0.16) * (band * 0.25 + edge * 0.35) * (0.3 + 0.7 * on);
    layer(acc, cover, capC, smoothstep(0.5, -0.5, capD));

    // Tassel: swings a little behind the lantern, a cord then a flared fringe.
    float2 t = rot(q - float2(0.0, -0.695 * H), -(0.6 * a + 0.12 * sin(time * 2.1 + i * 0.9)));
    float down = -t.y / H;
    float wid = lerp(0.012, 0.05, smoothstep(0.08, 0.26, down)) * H;
    float tas = (abs(t.x) - max(wid, 0.6)) * step(0.0, down) * step(down, 0.27);
    float strands = 0.75 + 0.25 * sin(t.x / max(wid, 0.6) * 9.0);
    float3 tc = silk * lerp(0.25, 0.9 * strands, on) + 0.03;
    layer(acc, cover, tc, smoothstep(0.5, -0.5, tas) * step(0.0, down) * step(down, 0.27) * smoothstep(0.27, 0.22, down));
}

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

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

    // Far off along the river: out-of-focus lanterns over the played part, drifting slowly.
    float2 bq = px / (0.5 * H) + float2(time * 0.15, 0.0);
    float2 bc = floor(bq);
    float bh = hash1(bc.x * 7.31 + bc.y * 13.7);
    float2 bo = float2(hash1(bh * 91.0), hash1(bh * 57.0)) - 0.5;
    float bd = length(frac(bq) - 0.5 - bo * 0.3);
    float br = 0.16 + 0.14 * hash1(bh * 23.0);
    float bok = smoothstep(br, br - 0.05, bd) * (0.7 + 0.3 * smoothstep(br - 0.1, br, bd));
    layer(acc, cover, silkOf(floor(bh * 9.0)), bok * step(0.6, bh) * played * (0.14 + 0.08 * level));

    // The cord: a catenary sag between the hanging points, warm where the lanterns are lit.
    float f = frac(px.x / sp - 0.5);
    float cordY = H * 0.96 - H * 0.09 * 4.0 * f * (1.0 - f);
    float cord = smoothstep(max(0.6, 0.02 * H) + 0.5, max(0.6, 0.02 * H) - 0.5, abs(px.y - cordY));
    layer(acc, cover, lerp(float3(0.22, 0.13, 0.08), float3(0.95, 0.7, 0.4), 0.6 * played), cord);

    // The two nearest lanterns: 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);
    lantern(acc, cover, px, j, sp, H, head);
    lantern(acc, cover, px, i, sp, H, head);

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