Liquid Fill
của MusicBar, thanh tiến trình
Ống thủy tinh đổ đầy nước bóng loáng tới chỗ đang phát, mặt nước gợn theo nhạc, bọt khí nổi lên, cứ tới nhịp là sóng dội ngược lại và bắn vài giọt ra phía trước.
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 13d1738e1dcda750601a8ee797b842cdca5c8c9933be92256d73e5bf23d69256
//! name: Liquid Fill
//! author: MusicBar
//! kind: progress
//! description: A glass tube fills with glossy liquid up to the playhead, its surface rippling with the music, bubbles rising and a splash running back on every beat.
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);
}
// Smooth maximum: rounds the corner where the surface meets the front of the liquid.
float smax(float a, float b, float k)
{
float h = saturate(0.5 + 0.5 * (a - b) / k);
return lerp(b, a, h) + k * h * (1.0 - h);
}
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 mid = u * 0.5;
float r = u * 0.3; // inner radius of the tube
float x0 = u * 0.4;
float x1 = size.x - u * 0.4;
// The tube: a long capsule, negative inside.
float2 tq = float2(px.x - clamp(px.x, x0, x1), px.y - mid);
float tube = length(tq) - r;
float n = clamp((px.y - mid) / r, -1.0, 1.0); // -1 at the bottom of the glass, 1 at the top
// The music: bass near the front, treble further back.
float behind = max(head - px.x, 0.0);
float spec = spectrum(saturate(behind / (u * 16.0)));
float bass = spectrum(0.03);
// The surface: rolling waves that grow with the spectrum, a splash ripple running back from the
// front on the beat, and a little bulge where the liquid piles up at the front.
float amp = u * (0.02 + 0.08 * spec) * (0.6 + 0.7 * level);
float surf = mid + r * 0.42 + amp * sin(px.x / u * 3.6 - time * 4.5) + u * 0.015 * sin(px.x / u * 9.0 + time * 3.1);
surf += beat * u * 0.09 * sin(behind / u * 6.5 + time * 16.0) * exp(-behind / (u * 2.2));
surf += u * (0.04 + 0.05 * bass) * exp(-behind / (u * 0.7));
// The front leans back at the top, wobbles, and surges forward on the beat.
float front = head + u * (0.1 - 0.2 * (n + 1.0) * 0.5 + 0.05 * sin(time * 5.0 + n * 2.5) * (0.5 + level) + 0.08 * beat);
float liquid = max(smax(px.y - surf, px.x - front, u * 0.12), tube + 1.2);
float inLiquid = smoothstep(0.6, -0.6, liquid);
float3 acc = float3(0, 0, 0);
float cover = 0;
// The glass itself: clear in the middle, a little denser toward the walls, so the empty tube reads.
float glass = smoothstep(0.6, -0.6, tube);
layer(acc, cover, lerp(colorC.rgb, float3(1, 1, 1), 0.5), glass * (0.03 + 0.1 * pow(abs(n), 6.0)));
// Light from the liquid spilling into the empty glass ahead of it.
float spill = exp(-max(liquid, 0.0) / (u * 0.8)) * smoothstep(head - u * 0.4, head + u * 0.1, px.x) * glass;
layer(acc, cover, colorA.rgb, spill * (0.12 + 0.18 * level + 0.15 * beat));
// The liquid: colorA at the surface to colorB at the bottom, rounded like a cylinder, with a slow shimmer.
float depth = saturate((surf - px.y) / (r * 1.4));
float3 liq = lerp(colorA.rgb, colorB.rgb, depth);
liq *= 0.6 + 0.4 * sqrt(saturate(1.0 - n * n * 0.9));
liq += colorA.rgb * 0.12 * (0.5 + 0.5 * sin(px.x / u * 2.3 - time * 1.7 + sin(px.y / u * 7.0 + time)));
layer(acc, cover, liq, inLiquid * 0.92);
// Gloss: a soft band of light riding just under the surface, and light gathered along the bottom.
float gloss = smoothstep(u * 0.06, 0.0, abs(px.y - (surf - u * 0.11))) * smoothstep(0.2, 0.6, sin(px.x / u * 1.25 + 1.0) * 0.5 + 0.5);
layer(acc, cover, lerp(colorA.rgb, float3(1, 1, 1), 0.75), gloss * inLiquid * 0.35);
float caustic = smoothstep(u * 0.05, 0.0, abs(px.y - (mid - r * 0.8))) * (0.6 + 0.4 * sin(px.x / u * 5.0 - time * 3.0));
layer(acc, cover, lerp(colorB.rgb, colorA.rgb, 0.6), caustic * inLiquid * 0.45);
// Bubbles rising through the liquid: one per narrow column, more when the song is loud.
float cw = u * 0.24;
float col = floor(px.x / cw);
float hb = hash1(col * 3.17 + 1.3);
float rb = u * (0.025 + 0.03 * hash1(col * 7.1 + 2.0));
float by = mid - r + rb + frac(time * (0.25 + 0.35 * hb) + hb * 9.0) * (2.0 * r);
float bx = (col + 0.5) * cw + sin(time * 3.0 + hb * 30.0) * cw * 0.18;
float2 bd = px - float2(bx, by);
float bl = length(bd);
float on = step(hb, 0.3 + 0.45 * level + 0.3 * beat) * inLiquid * smoothstep(0.0, u * 0.05, surf - by);
float ring = smoothstep(1.1, 0.2, abs(bl - rb)) * 0.7 + smoothstep(rb, rb * 0.3, bl) * 0.12;
float shineB = smoothstep(rb * 0.45, rb * 0.1, length(bd - float2(-0.35, 0.35) * rb));
layer(acc, cover, lerp(colorA.rgb, float3(1, 1, 1), 0.7), on * ring);
layer(acc, cover, float3(1, 1, 1), on * shineB);
// On the beat a few drops spray forward off the front and fall into the empty glass.
float t = 1.0 - beat;
float drops = 0;
[unroll] for (int i = 0; i < 3; i++)
{
float2 dp = float2(head + u * (0.05 + (0.22 + 0.26 * i) * t), mid + r * 0.4 + u * (0.5 - 0.18 * i) * t - u * 0.9 * t * t);
float dr = u * (0.045 - 0.01 * i);
drops += smoothstep(dr + 0.5, dr - 0.5, length(px - dp));
}
layer(acc, cover, lerp(colorA.rgb, float3(1, 1, 1), 0.3), drops * step(0.04, beat) * smoothstep(0.5, -0.5, tube + 1.0));
// The meniscus: a bright line just under the surface and along the front.
float edge = exp(-abs(liquid + 1.5) / 1.1) * inLiquid;
layer(acc, cover, lerp(colorA.rgb, float3(1, 1, 1), 0.65), edge * (0.65 + 0.35 * beat));
// Glass graduations printed along the top, every bar height and a longer one every five.
float tick = px.x / u;
float tickLen = lerp(0.14, 0.3, step(frac(floor(tick + 0.5) / 5.0 + 0.01), 0.02)) * r;
float tickOn = smoothstep(1.0, 0.3, abs(frac(tick + 0.5) - 0.5) * u) * step(mid + r - tickLen, px.y) * glass;
layer(acc, cover, float3(1, 1, 1), tickOn * 0.28);
// The glass highlight: a long streak along the top, broken into slices, and a faint one below.
float slice = frac(px.x / (u * 7.0));
float streak = smoothstep(u * 0.05, 0.0, abs(px.y - (mid + r * 0.62))) * smoothstep(0.84, 0.8, slice) * smoothstep(0.0, 0.03, slice);
streak *= smoothstep(0.0, u * 0.3, px.x - x0 + r) * smoothstep(0.0, u * 0.3, x1 + r - px.x);
layer(acc, cover, float3(1, 1, 1), streak * (0.3 + 0.25 * inLiquid));
float lowStreak = smoothstep(u * 0.035, 0.0, abs(px.y - (mid - r * 0.7))) * glass;
layer(acc, cover, lerp(colorC.rgb, float3(1, 1, 1), 0.6), lowStreak * 0.12);
// The glass wall: a crisp outline, lighter on top where it catches the light, with a dark
// hairline outside it so the tube still shows on a light taskbar.
layer(acc, cover, float3(0.08, 0.1, 0.14), smoothstep(1.2, 0.2, abs(tube - 1.8)) * 0.35);
float wall = smoothstep(1.3, 0.3, abs(tube - 0.6));
layer(acc, cover, lerp(colorC.rgb, float3(1, 1, 1), 0.55), wall * (0.38 + 0.3 * saturate(n)));
return paint(acc / max(cover, 0.001), cover);
}