Blossom Branch
của MusicBar, thanh tiến trình
Cành đào ngày Tết mọc tới chỗ đang phát, hoa năm cánh nở dần, cánh hoa bay lả tả theo gió và rụng nhiều hơn mỗi khi tới nhịp, đổi sang màu vàng là thành cành mai.
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 c7dd314bea8cedb7adad7a45d3911dfe72b9ad7e095b3ca4935c8c43dbac38d9
//! name: Blossom Branch
//! author: MusicBar
//! kind: progress
//! description: A gnarled Tet branch grows to the playhead and bursts into five-petal blossoms, with petals drifting off on the breeze and a flurry on every beat.
// A steady random number for a cell.
float hash1(float n) { n = frac(n * 0.1031); n *= n + 33.33; n *= n + n; return frac(n); }
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);
float s = sin(a);
return float2(c * p.x + s * p.y, -s * p.x + c * p.y);
}
// The branch's height along the bar: slow bends plus angular kinks so it looks gnarled.
float branchY(float x, float u)
{
float k = abs(frac(x / (u * 1.7)) - 0.5) * 4.0 - 1.0;
return u * (0.42 + 0.11 * sin(x / u * 0.75 + 0.5) + 0.05 * sin(x / u * 2.1 + 1.3) + 0.045 * k);
}
// Distance to a twig from a to b that thins toward its tip.
float twig(float2 p, float2 a, float2 b, float r0, float r1)
{
float2 pa = p - a;
float2 ba = b - a;
float h = saturate(dot(pa, ba) / max(dot(ba, ba), 0.001));
return length(pa - ba * h) - lerp(r0, r1, h);
}
// One blossom: five round petals with a notch at each tip, opening from a bud as open goes 0 to 1.
void blossom(inout float3 acc, inout float cover, float2 p, float R, float open, float spin)
{
float2 q = rot(p, spin);
float r = length(q);
float a = atan2(q.y, q.x);
float k = floor(a / 1.2566 + 0.5);
float pa = a - k * 1.2566;
float2 pq = r * float2(cos(pa), sin(pa));
float pr = R * lerp(0.17, 0.5, open);
float pc = R * lerp(0.08, 0.5, open);
float d = max(length(pq - float2(pc, 0)) - pr, R * 0.09 - length(pq - float2(pc + pr * 1.02, 0)));
float body = smoothstep(0.6, -0.6, d);
float3 c = lerp(colorA.rgb * lerp(0.7, 0.85, open), lerp(colorB.rgb, float3(1, 1, 1), 0.3), smoothstep(0.1, 1.0, r / R) * open);
c += float3(1, 1, 1) * smoothstep(R * 0.1, 0.0, length(q - float2(-0.08, 0.1) * R)) * (1.0 - open) * 0.6;
c *= 1.0 - 0.3 * smoothstep(R * 0.03, 0.0, abs(abs(pa) - 0.6283) * r) * step(R * 0.25, r) * open;
c *= 1.0 - 0.25 * smoothstep(-2.0, 0.0, d);
layer(acc, cover, c, body);
// The heart: a colorC center ringed with tiny stamen dots.
float sa = frac(a / 0.6283) - 0.5;
float stamen = smoothstep(R * 0.07, R * 0.03, length(float2(sa * 0.6283 * r, r - R * 0.3)));
float heart = smoothstep(R * 0.17, R * 0.1, r);
layer(acc, cover, lerp(colorC.rgb, float3(1, 1, 1), 0.2), (heart * 0.9 + stamen) * smoothstep(0.35, 0.7, open));
}
float4 main(float2 uv : TEXCOORD) : COLOR
{
float2 px = float2(uv.x * size.x, (1.0 - uv.y) * size.y); // pixels: along the bar, and up the screen so gravity holds on a top taskbar too
float u = size.y;
float head = progress * size.x;
float sway = sin(time * 1.4) * u * 0.03 * (1.0 + level); // the young end sways in the breeze
float3 acc = float3(0, 0, 0);
float cover = 0;
// The main branch, thick and old at the start, thin at the growing tip.
float by = branchY(px.x, u) + sway * exp(-max(head - px.x, 0.0) / (u * 2.0));
float slope = (branchY(px.x + 1.0, u) - branchY(px.x - 1.0, u)) * 0.5;
float th = u * (0.028 + 0.05 * saturate((head - px.x) / (u * 7.0)));
float2 tipP = float2(head, branchY(head, u) + sway);
float bd = px.x > head ? length(px - tipP) - u * 0.028 : abs(px.y - by) / sqrt(1.0 + slope * slope) - th;
// Ahead of the playhead a faint dotted ghost of the branch to come; behind it a soft blossom glow.
float ghost = smoothstep(1.2, 0.2, abs(px.y - by)) * step(head + u * 0.3, px.x) * step(frac(px.x / (u * 0.18)), 0.5);
layer(acc, cover, lerp(colorA.rgb, float3(1, 1, 1), 0.3), ghost * 0.22);
float glow = exp(-abs(px.y - by) / (u * 0.25)) * smoothstep(head + u * 0.4, head - u * 0.6, px.x);
layer(acc, cover, colorA.rgb, glow * (0.1 + 0.12 * level));
// Twigs: one per cell, reaching forward and up or down from the branch, each tipped with a flower.
float cw = u * 1.15;
float cell = floor(px.x / cw);
float twigD = 1e4;
float2 tips[2]; // each twig's tip and how open its flower is
float tipOpen[2];
[unroll] for (int i = 0; i < 2; i++)
{
float k = cell - i;
float x0 = (k + 0.15 + 0.5 * hash1(k * 3.3)) * cw;
float grow = saturate((head - x0) / (u * 1.2));
float ang = (0.55 + 0.45 * hash1(k * 1.9)) * (hash1(k * 7.7 + 1.0) < 0.55 ? 1.0 : -1.0);
float len = u * (0.28 + 0.16 * hash1(k * 5.1)) * grow;
float2 a0 = float2(x0, branchY(x0, u) + sway * exp(-max(head - x0, 0.0) / (u * 2.0)));
tips[i] = a0 + float2(cos(ang), sin(ang)) * len;
twigD = min(twigD, twig(px, a0, tips[i], u * 0.03, u * 0.012) + step(grow, 0.0) * 1e4);
tipOpen[i] = saturate((head - x0) / (u * 2.5) - 0.3) * step(u * 0.05, len);
}
// The bark: dark wood with a warm rim of light along the top and fine grain.
float wood = min(bd, twigD);
float woodA = smoothstep(0.6, -0.6, wood);
float grain = 0.85 + 0.15 * sin(px.x / u * 40.0 + sin(px.y / u * 9.0) * 2.0);
float rimLit = smoothstep(-th * 0.2, th * 1.2, px.y - by) * step(bd, twigD);
layer(acc, cover, float3(0.2, 0.12, 0.09) * grain + float3(0.35, 0.22, 0.15) * rimLit, woodA);
layer(acc, cover, float3(0.75, 0.55, 0.42), smoothstep(1.0, 0.0, abs(wood + 0.6)) * 0.45 * woodA);
// Blossoms along the branch: newest near the tip are still buds, older ones wide open,
// breathing a little with the music.
float fw = u * 0.6;
float fc = floor(px.x / fw);
float pulse = 1.0 + 0.1 * level + 0.12 * beat;
[unroll] for (int j = -1; j <= 1; j++)
{
float k = fc + j;
float h = hash1(k * 4.1 + 2.0);
float fx = (k + 0.2 + 0.6 * hash1(k * 9.3)) * fw;
float side = (frac(k * 0.5) < 0.25 ? 1.0 : -1.0) * (0.06 + 0.12 * hash1(k * 6.7));
float2 fp = float2(fx, branchY(fx, u) + sway * exp(-max(head - fx, 0.0) / (u * 2.0)) + side * u);
float R = u * (0.17 + 0.06 * hash1(k * 2.3)) * pulse;
if (h < 0.78 && fx < head - u * 0.05) blossom(acc, cover, px - fp, R, saturate((head - fx) / (u * 2.2) - 0.15), h * 6.28 + time * 0.1);
}
if (tipOpen[0] > 0.0) blossom(acc, cover, px - tips[0], u * 0.18 * pulse, tipOpen[0], cell);
if (tipOpen[1] > 0.0) blossom(acc, cover, px - tips[1], u * 0.18 * pulse, tipOpen[1], cell + 2.0);
// The growing tip carries a fat bud that swells and glows on the beat.
float2 budP = tipP + float2(u * 0.07, u * 0.01);
float2 bq = rot(px - budP, 0.25);
layer(acc, cover, colorC.rgb, exp(-length(px - budP) / (u * 0.12)) * (0.25 + 0.45 * beat));
float3 budC = colorA.rgb * 0.8 + smoothstep(u * 0.04, 0.0, length(bq - float2(u * 0.01, u * 0.03))) * 0.5;
layer(acc, cover, budC, smoothstep(0.6, -0.6, length(float2(bq.x * 0.75, bq.y)) - u * (0.065 + 0.02 * beat)));
layer(acc, cover, float3(0.25, 0.32, 0.12), smoothstep(0.6, -0.6, length(px - tipP - float2(u * 0.02, 0.0)) - u * 0.035));
// Petals let go and drift back and down; more of them fly when the music is loud or on the beat.
float2 dp = px + float2(time * u * 0.55, time * u * 0.22);
dp.y += sin(dp.x / u * 1.3 + time) * u * 0.08;
float2 dc = floor(dp / (u * 0.55));
float dh = hash1(dc.x * 13.7 + dc.y * 3.1);
float2 dq = dp - (dc + 0.25 + 0.5 * float2(hash1(dc.x + dc.y * 7.0), hash1(dc.y * 3.0 - dc.x))) * u * 0.55;
dq = rot(dq, time * (1.0 + 2.0 * dh) + dh * 6.0);
dq.x /= max(abs(cos(time * 2.0 + dh * 9.0)), 0.3);
float petal = smoothstep(0.6, -0.6, length(float2(dq.x * 0.8, dq.y * 1.4)) - u * 0.055);
float drift = step(dh, 0.12 + 0.25 * level + 0.45 * beat) * smoothstep(head + u * 0.4, head - u * 0.4, px.x);
layer(acc, cover, lerp(colorA.rgb, colorB.rgb, 0.5 + 0.4 * dq.y / (u * 0.05)), petal * drift * 0.9);
return paint(acc / max(cover, 0.001), cover);
}