Lucky Firecrackers

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

Dây pháo đỏ ngày Tết nổ lách tách tới chỗ đang phát, chớp trắng, tóe lửa, xác pháo bay tứ tung, tới nhịp là nổ dồn hơn, phía sau còn khói với xác pháo đỏ rải đầy.

10 lượt tải Tải file, 8.9 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 c3c520a308a069a8f5278f19b603989978e09216d0ca9acef695f5e1045b2949

//! name: Lucky Firecrackers
//! author: MusicBar
//! kind: progress
//! description: A Tet string of red firecrackers pops its way to the playhead in white flashes, sparks and flying red paper, harder on every beat, leaving smoke and confetti behind.

// A steady random number for a cell.
float hash2(float2 p)
{
    float3 q = frac(float3(p.x, p.y, p.x) * 0.1031);
    q += dot(q, q.yzx + 33.33);
    return frac((q.x + q.y) * q.z);
}

// Smooth value noise.
float vnoise(float2 p)
{
    float2 i = floor(p);
    float2 f = smoothstep(0.0, 1.0, frac(p));
    return lerp(lerp(hash2(i), hash2(i + float2(1, 0)), f.x), lerp(hash2(i + float2(0, 1)), hash2(i + float2(1, 1)), f.x), f.y);
}

// Distance to a rounded box around the origin (half size b, corner radius r), and a turn by angle a.
float box(float2 p, float2 b, float r)
{
    float2 q = abs(p) - b + r;
    return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - r;
}
float2 turn(float2 p, float a) { return float2(cos(a) * p.x + sin(a) * p.y, cos(a) * p.y - sin(a) * p.x); }

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);
}

// One side of the string: firecrackers sticking up (lane 1) or down (lane -1) off the cord, leaning back.
// The ones next in line tremble; the ones the playhead has passed are gone.
void cracker(inout float3 acc, inout float cover, float2 P, float lane, float yc, float H, float head)
{
    float sp = 0.27 * H;
    float off = 0.0675 * H * (1.0 - lane);
    float2 dir = normalize(float2(-0.3, lane));
    float i = floor((P.x + 0.3 * (P.y - yc) * lane - off) / sp + 0.5);
    float2 base = float2(i * sp + off, yc);
    base.x += smoothstep(1.5 * H, 0.0, base.x - head) * (hash2(float2(i + lane, floor(time * 30.0))) - 0.5) * 0.07 * H;
    float al = dot(P - base, dir);
    float ac = dot(P - base, float2(dir.y, -dir.x)) * lane;
    float rr = 0.075 * H, a0 = 0.07 * H, a1 = 0.37 * H;    // tube radius, inner and outer end
    float dist = box(float2(ac, al - (a0 + a1) * 0.5), float2(rr, (a1 - a0) * 0.5), rr * 0.5);
    float u = (al - a0) / (a1 - a0);
    // A glossy red tube with gold bands, a darker crimped end and a dark rim that reads on light taskbars.
    float n = sqrt(saturate(1.0 - ac * ac / (rr * rr)));
    float gloss = pow(saturate(1.0 - abs(ac / rr + 0.4) * 2.6), 2.0);
    float band = step(0.64, u) * step(u, 0.78) + step(0.08, u) * step(u, 0.15);
    float3 c = lerp(colorA.rgb, colorC.rgb, band) * (0.32 + 0.75 * n) * (0.88 + 0.24 * hash2(float2(i, lane * 5.0)));
    c = c * (1.0 - 0.4 * step(0.93, u)) + gloss * 0.5 * lerp(float3(1.0, 0.8, 0.75), float3(1.0, 1.0, 0.8), band);
    c *= 1.0 - 0.5 * smoothstep(-1.8, -0.2, dist);
    // The fuse tip poking out of the far end.
    float wick = smoothstep(0.5, -0.5, abs(ac) - max(0.55, 0.018 * H)) * step(a1 - 2.0, al) * step(al, a1 + 0.08 * H);
    float alive = step(head + 0.08 * H, base.x);
    layer(acc, cover, lerp(float3(0.3, 0.27, 0.25), float3(0.95, 0.9, 0.78), smoothstep(a1, a1 + 0.08 * H, al)), wick * alive);
    layer(acc, cover, c, smoothstep(0.5, -0.5, dist) * alive);
}

// One pop just behind the playhead: a white flash and rays, sparks flying out and dropping, a scrap of red paper.
void pop(inout float3 acc, inout float cover, float2 P, float k, float H, float head, float power)
{
    float T = 0.17 + 0.04 * k;
    float ph = time / T + k * 0.37;
    float id = floor(ph), age = frac(ph) * T;
    float s1 = hash2(float2(id, k * 7.1)), s3 = hash2(float2(k, id * 1.7));
    float2 c = float2(head - (0.05 + 0.55 * s1) * H, (0.5 + (hash2(float2(id + 3.3, k)) - 0.5) * 0.5) * H);
    float str = max((0.65 + 0.4 * s3) * power, 0.02);
    float fade = smoothstep(T, T * 0.55, age) * step(0.01, power);
    float dist = length(P - c);
    float a = atan2(P.y - c.y, P.x - c.x);
    float rays = pow(abs(cos(a * 3.0 + s1 * 6.0)), 16.0) * exp(-dist / (0.5 * H * str)) * exp(-age * 16.0);
    layer(acc, cover, lerp(colorC.rgb, float3(1, 1, 1), 0.45), (exp(-dist / (0.3 * H * str)) * exp(-age * 15.0) * 0.6 + rays) * fade);
    layer(acc, cover, float3(1, 1, 1), exp(-dist / (0.12 * H * str)) * exp(-age * 22.0) * 1.6 * fade);

    // The spark flying in this pixel's direction, drawn as a short streak with gravity.
    float b = floor((a + 3.14159) / 6.28318 * 9.0);
    float hb = hash2(float2(b, id + k * 13.0));
    float ang = (b + 0.2 + 0.6 * hb) / 9.0 * 6.28318 - 3.14159;
    float2 vel = float2(cos(ang), sin(ang)) * H * (3.0 + 3.0 * hb) * str / 7.0;
    float t0 = max(age - 0.035, 0.0);
    float2 e1 = c + vel * (1.0 - exp(-age * 7.0)) - float2(0, 3.0 * H * age * age);
    float2 e0 = c + vel * (1.0 - exp(-t0 * 7.0)) - float2(0, 3.0 * H * t0 * t0);
    float hh = saturate(dot(P - e0, e1 - e0) / max(dot(e1 - e0, e1 - e0), 0.001));
    float spark = smoothstep(0.6, -0.4, length(P - e0 - (e1 - e0) * hh) - max(0.7, 0.022 * H)) * (0.35 + 0.65 * hh);
    layer(acc, cover, lerp(colorC.rgb, float3(1, 1, 0.9), hh), spark * fade);

    // A scrap of red paper tumbling out.
    float b2 = floor((a + 3.14159) / 6.28318 * 5.0 + s3);
    float h2 = hash2(float2(b2, id * 2.3 + k));
    float ang2 = (b2 - s3 + 0.5 + (h2 - 0.5) * 0.6) / 5.0 * 6.28318 - 3.14159;
    float2 at = c + float2(cos(ang2), sin(ang2)) * H * (2.4 + 2.0 * h2) * str * (1.0 - exp(-age * 5.0)) / 5.0 - float2(0, 2.0 * H * age * age);
    float spin = h2 * 6.28 + age * 30.0;
    float bit = smoothstep(0.5, -0.5, box(turn(P - at, spin), float2(0.055, 0.012 + 0.025 * abs(cos(spin * 1.3))) * H, 0.0));
    layer(acc, cover, colorA.rgb * (0.65 + 0.45 * abs(sin(spin))), bit * fade);
}

float4 main(float2 uv : TEXCOORD) : COLOR
{
    float H = size.y;
    float2 px = float2(uv.x * size.x, rise(uv) * size.y);   // pixels: along the bar, up from the screen edge
    float head = progress * size.x;
    float d = (head - px.x) / H;                           // bar heights behind the playhead
    float yc = H * (0.5 + 0.03 * sin(px.x / H * 0.7 + time * 1.3));

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

    // Smoke puffs drifting up and back from where the string has popped.
    float2 sq = px / H + float2(time * 0.3, -time * 0.45);
    float sm = vnoise(sq * 1.7) * 0.65 + vnoise(sq * 3.6 + 5.0) * 0.35;
    float puff = smoothstep(0.45, 0.72, sm) * smoothstep(-0.4, 0.5, d) * exp(-max(d, 0.0) / 6.0);
    layer(acc, cover, lerp(float3(0.55, 0.53, 0.52), float3(0.92, 0.9, 0.88), smoothstep(0.5, 0.85, sm)), puff * 0.36);

    // Red paper confetti covering the ground behind, the odd gold scrap among it.
    float2 gc = floor(px / (0.1 * H));
    float gr = hash2(gc + 17.0);
    float2 gl = turn(px - (gc + 0.25 + 0.5 * float2(hash2(gc + 1.3), hash2(gc + 2.9))) * 0.1 * H, gr * 40.0);
    float scrap = smoothstep(0.5, -0.5, box(gl, float2(0.042, 0.022) * H * (0.7 + 0.6 * frac(gr * 13.0)), 0.3));
    float3 paper = lerp(colorA.rgb * (0.5 + 0.55 * frac(gr * 7.0)), colorC.rgb, step(frac(gr * 29.0), 0.12));
    layer(acc, cover, paper, scrap * step(gr, 0.75 * smoothstep(0.38 * H, 0.0, px.y) * smoothstep(-0.2, 1.2, d)));

    // Scraps still fluttering down behind the playhead, more of them on the beat.
    float2 fq = px / H * float2(5.0, 4.0) + float2(0.25 * sin(time * 2.0 + px.y / H * 3.0), time * 1.1);
    float fr = hash2(floor(fq) + 41.0);
    float flip = time * 7.0 + fr * 20.0;
    float fall = smoothstep(0.5, -0.5, box(turn((frac(fq) - 0.5) * H / float2(5.0, 4.0), flip * 0.4), float2(0.045, 0.008 + 0.022 * abs(cos(flip))) * H, 0.0));
    float many = (0.12 + 0.2 * beat + 0.1 * level) * smoothstep(-0.3, 0.3, d) * exp(-max(d, 0.0) / 4.0);
    layer(acc, cover, colorA.rgb * (0.6 + 0.45 * abs(sin(flip))), fall * step(fr, many));

    // The braided cord ahead of the playhead, red and gold strands twisting, and the firecrackers on both sides of it.
    float cw = 0.065 * H, cy = (px.y - yc) / cw, twist = (px.x + (px.y - yc) * 1.3) / (0.11 * H);
    float3 cord = lerp(colorA.rgb * 0.8, colorC.rgb, step(0.5, frac(twist * 0.5))) * (0.3 + 0.7 * sqrt(saturate(1.0 - cy * cy))) * (0.55 + 0.45 * sin(frac(twist) * 3.14159));
    layer(acc, cover, cord, smoothstep(cw + 0.5, cw - 0.5, abs(px.y - yc)) * step(head, px.x));
    cracker(acc, cover, px, 1.0, yc, H, head);
    cracker(acc, cover, px, -1.0, yc, H, head);

    // A warm light thrown by the pops, and the burning fuse where the string is going off.
    float glow = exp(-length((px - float2(head - 0.25 * H, yc)) * float2(0.7, 1.0)) / (0.45 * H));
    layer(acc, cover, lerp(colorC.rgb, float3(1.0, 0.95, 0.85), 0.4), glow * (0.18 + 0.4 * beat));
    layer(acc, cover, lerp(colorC.rgb, float3(1, 1, 1), 0.6), exp(-length(px - float2(head, yc)) / (0.07 * H)) * (0.8 + 0.2 * sin(time * 50.0)));

    // Rapid pops just behind it; the third and fourth join in when the music is loud or on the beat.
    float power = 0.75 + 0.4 * level + 0.7 * beat;
    [unroll] for (int k = 0; k < 4; k++)
        pop(acc, cover, px, k, H, head, power * step(k * 0.225 - 0.25, level * 0.6 + beat * 0.8));

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