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