Wildfire

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

Lửa cháy thật dọc phần đã phát, bùng cao nhất ở chỗ đang phát rồi lụi dần thành than hồng phía sau, bass càng mạnh lửa càng bốc cao, tới nhịp là tàn lửa bay lên.

28 lượt tải Tải file, 7.3 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 651d2bae84c19ab2e6efd027d3c78264b1debe4558b2fa04626f0e757794d1af

//! name: Wildfire
//! author: MusicBar
//! kind: progress
//! description: Real flames burn along the played part, roaring tallest at the playhead, and settle into glowing coals and drifting embers behind it.

// 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 = frac(p);
    f = f * f * (3.0 - 2.0 * f);
    float a = hash2(i);
    float b = hash2(i + float2(1, 0));
    float c = hash2(i + float2(0, 1));
    float d = hash2(i + float2(1, 1));
    return lerp(lerp(a, b, f.x), lerp(c, d, f.x), f.y);
}

// Four octaves of turbulence, each one turned a little so no grid shows.
float fbm(float2 p)
{
    float v = 0;
    float a = 0.5;
    [unroll] for (int i = 0; i < 4; i++)
    {
        v += a * vnoise(p);
        p = float2(1.6 * p.x + 1.2 * p.y, -1.2 * p.x + 1.6 * p.y) + float2(3.1, 7.7);
        a *= 0.5;
    }
    return v / 0.9375;
}

// Blackbody heat: dull red, red, orange, yellow, near white. The palette tints the cool tips, and the middle a little.
float3 heat(float t)
{
    float3 c = lerp(float3(0.30, 0.02, 0.00), float3(0.80, 0.09, 0.02), smoothstep(0.0, 0.3, t));
    c = lerp(c, float3(1.00, 0.36, 0.04), smoothstep(0.25, 0.6, t));
    c = lerp(c, float3(1.00, 0.72, 0.22), smoothstep(0.55, 0.88, t));
    c = lerp(c, float3(1.00, 0.96, 0.84), smoothstep(0.9, 1.2, t));
    float tips = smoothstep(0.5, 0.1, t);
    float mids = smoothstep(0.2, 0.45, t) * smoothstep(0.9, 0.6, t);
    return lerp(c, max(c, colorA.rgb * (0.35 + 0.9 * t)), 0.5 * tips + 0.15 * mids);
}

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

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 h = px.y / H;
    float head = progress * size.x;
    float d = (head - px.x) / H;                           // bar heights behind the playhead
    float near = exp(-max(d, 0.0) / 2.5);

    // Bass feeds the flames at the playhead, the treble the ones far back.
    float s = spectrum(saturate(d / 14.0));
    float bass = spectrum(0.03);

    // Flames lean forward at the front and sway a little; db is where this flame stands on the ground.
    float lean = h * (0.1 + 0.5 * near * (0.45 + 0.75 * h) + 0.08 * sin(time * 1.3 + px.x / H * 0.7));
    float db = d + lean;
    float lit = smoothstep(-0.25, 0.35, db);                // 0 where the fire has not arrived yet

    // How tall the fire stands: roaring behind the playhead, then patchy low flames over the coals.
    float patches = smoothstep(0.35, 0.75, vnoise(float2(px.x / H * 0.9 - time * 0.15, time * 0.35)));
    float tall = exp(-max(db - 0.5, 0.0) / 3.5) * (0.8 + 0.4 * bass + 0.35 * beat * near);
    float calm = exp(-max(d, 0.0) / 14.0);                     // far back the flames die down to coals
    float env = 0.03 + 0.05 * calm + 0.22 * patches * (0.6 + 0.6 * s) * (0.3 + 0.7 * calm) + tall * (0.5 + 0.6 * s);

    // Turbulence scrolling up fast, warped so the tongues twist and split.
    float t = time * 2.8;
    float2 p = float2((px.x / H - lean) * 3.4, h * 1.7 - t);
    float2 w = float2(vnoise(p * 0.5 + float2(0.0, t * 0.3)), vnoise(p * 0.5 + float2(5.2, 1.3)));
    float n = fbm(p + (w - 0.5) * 2.0);

    // The flame field: hot at the base, broken into tongues higher up.
    float hn = h / env;
    float I = (1.0 - hn) * 0.9 + (n - 0.5) * (0.8 + 1.8 * hn);
    I = I * lerp(0.75, 1.15, near) * (1.0 + 0.15 * beat) - (1.0 - lit) * 3.0;

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

    // Dry grass waiting ahead of the fire in uneven tufts; the blades nearest the flames catch and glow.
    float gw = H * 0.075;
    float gi = floor(px.x / gw);
    float gr = hash2(float2(gi, 3.7));
    float tuft = vnoise(float2(gi * 0.23, 5.0));
    float gh = H * (0.06 + 0.3 * gr * gr + 0.18 * tuft) * step(0.18, hash2(float2(gi, 1.3)));
    float bx = (gi + 0.25 + 0.5 * hash2(float2(gi, 9.1))) * gw + px.y * (0.5 * gr - 0.22 + 0.06 * sin(time * 1.8 + gi * 0.6));
    float gy = saturate(px.y / max(gh, 0.5));
    float half0 = 0.3 + (1.0 - gy) * H * 0.024;
    float blade = smoothstep(half0 + 0.5, half0 - 0.5, abs(px.x - bx)) * step(px.y, gh);
    float ahead = smoothstep(0.05, -0.25, d);
    float scorch = exp(min(d, 0.0) * 1.3);
    float tipBurn = scorch * scorch * smoothstep(0.25, 0.9, gy) * 1.3 * (0.7 + 0.3 * sin(time * 23.0 + gi * 3.0));
    float3 straw = lerp(float3(0.50, 0.40, 0.24), float3(0.78, 0.66, 0.40), gy);
    straw = lerp(straw, float3(1.0, 0.42, 0.08), scorch * 0.8);
    straw = lerp(straw, float3(1.0, 0.85, 0.5), saturate(tipBurn));
    layer(acc, cover, straw, blade * ahead * (0.4 + 0.6 * scorch));

    // Smoke curling off the older burn: thin wisps above the flame tips, lit warm from below.
    float wisp = 1.0 - abs(w.y * 1.6 + n * 0.6 - 1.1) * 5.0;
    float smoke = saturate(wisp) * smoothstep(env, env + 0.45, h) * smoothstep(0.8, 3.0, d) * smoothstep(1.0, 0.45, h);
    layer(acc, cover, lerp(float3(0.62, 0.56, 0.52), float3(0.9, 0.5, 0.3), smoothstep(env + 0.5, env, h)), smoke * 0.12);

    // Soft heat glow around the fire, flaring at the playhead on the beat.
    float glow = pow(saturate(I + 0.45), 2.0) * lit;
    layer(acc, cover, heat(0.4), glow * (0.3 + 0.3 * beat * near));
    float bloom = exp(-length(float2(px.x - head + 0.4 * H, px.y - 0.15 * H)) / (H * (0.45 + 0.3 * beat)));
    layer(acc, cover, heat(0.55), bloom * (0.16 + 0.3 * beat));

    // Glowing coals on the ground where the fire has passed: lumps with dark cracks, breathing.
    float2 cq = float2(px.x / H * 6.0, h * 9.0);
    float coal = vnoise(cq) * 0.6 + vnoise(cq * 2.4 + 4.0) * 0.4;
    float top = 0.1 + 0.07 * vnoise(float2(px.x / H * 4.0, 1.0));
    float bed = smoothstep(top + 0.5 / H, top - 0.5 / H, h) * smoothstep(0.0, 0.8, d);
    float glowCoal = smoothstep(0.45, 0.75, coal) * (0.7 + 0.3 * sin(time * 1.4 + coal * 11.0)) * (0.85 + 0.3 * level);
    layer(acc, cover, heat(0.2 + 0.7 * glowCoal), bed * (0.45 + 0.55 * smoothstep(0.3, 0.55, coal)));

    // The flames.
    layer(acc, cover, heat(I), smoothstep(0.0, 0.16, I));

    // Embers drifting up and back, flickering; more of them when it is loud and on the beat.
    float2 ep = px / H * 3.0 + float2(time * 0.8, -time * 2.4);
    float2 cell = floor(ep);
    float r = hash2(cell);
    float2 at = 0.2 + 0.6 * float2(hash2(cell + 3.1), hash2(cell + 7.3)) + float2(0.15 * sin(time * 3.0 + r * 30.0), 0.0);
    float2 rel = (frac(ep) - at) * H / 3.0;
    float dot0 = length(float2(rel.x + rel.y * 0.3, rel.y * 0.45));   // a short streak along the way it flies
    float er = max(0.9, H * 0.022);
    float live = (0.5 + 0.5 * sin(time * 19.0 + r * 60.0)) * step(r, 0.18 + 0.2 * level + 0.35 * beat)
               * smoothstep(-0.8, 0.2, d) * exp(-max(d, 0.0) / 5.0) * smoothstep(-0.05, 0.25, h);
    layer(acc, cover, lerp(float3(1.0, 0.45, 0.1), colorA.rgb, 0.5), 0.55 * exp(-dot0 / (er * 1.8)) * live);
    layer(acc, cover, lerp(float3(1.0, 0.85, 0.5), colorA.rgb, 0.25), smoothstep(er + 0.6, er - 0.4, dot0) * live);

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