perf(shader): hoist playhead texture tap; clamp neighbour sdRoundBox corner radius

This commit is contained in:
daniel-c-harvey
2026-06-15 23:55:16 -04:00
parent a9d6445881
commit 5011fb43f0
@@ -502,7 +502,7 @@ float smin(float a, float b, float k) {
// edge; y is screen-row time as before. Loudness at this row sets the attached
// half-width; loudness at neighbouring rows lets the metaball smooth-min coalesce
// vertically into a continuous liquid column rather than discrete per-row slabs.
float ribbonField(vec2 px, float nowY, float pixelsPerSecond, float maxHalfWidth, out float ampOut) {
float ribbonField(vec2 px, float nowY, float pixelsPerSecond, float maxHalfWidth, float playheadFeed, out float ampOut) {
float screenYTop = px.y;
float screenX = px.x;
@@ -546,8 +546,10 @@ float ribbonField(vec2 px, float nowY, float pixelsPerSecond, float maxHalfWidth
float dtRow = reach * maxHalfWidth / pixelsPerSecond; // xn-reach back to seconds
float ampUp = sampleAt(t - dtRow);
float ampDn = sampleAt(t + dtRow);
float up = sdRoundBox(vec2(xn, reach), vec2(max(ampUp, 0.001), reach), cornerR);
float dn = sdRoundBox(vec2(xn, -reach), vec2(max(ampDn, 0.001), reach), cornerR);
vec2 boxUp = vec2(max(ampUp, 0.001), reach);
vec2 boxDn = vec2(max(ampDn, 0.001), reach);
float up = sdRoundBox(vec2(xn, reach), boxUp, min(cornerR, min(boxUp.x, boxUp.y) - 1e-3));
float dn = sdRoundBox(vec2(xn, -reach), boxDn, min(cornerR, min(boxDn.x, boxDn.y) - 1e-3));
float k = BUBBLE_SMOOTHMIN_K * uBubblyness;
attached = smin(attached, smin(up, dn, k), k);
}
@@ -569,8 +571,8 @@ float ribbonField(vec2 px, float nowY, float pixelsPerSecond, float maxHalfWidth
// Spawn column: spread across the ribbon width, biased by loudness presence.
float colX = (seed * 2.0 - 1.0) * 0.8;
// Loudness feeding this blob's column at the now line — a louder mix sheds
// bigger blobs. Sampled once at the playhead (not per-pixel-varying).
float feed = sampleAt(uPlayheadSeconds);
// bigger blobs. playheadFeed is pre-computed once in main() (fragment-invariant).
float feed = playheadFeed;
float radius = (0.05 + 0.10 * seed) * (0.4 + 0.6 * feed) * uDetach;
// Rise phase: 0→1 over the blob's life, looping. Different phase offset per
// blob so they don't pulse in unison. Speed scales mildly with detach.
@@ -619,20 +621,25 @@ void main() {
return;
}
// Hoist the playhead-feed tap: sampleAt(uPlayheadSeconds) is fragment-invariant
// (depends only on a uniform) and would otherwise run inside the 7-iteration detach
// blob loop × 5 ribbonField calls = up to 35× per pixel. Compute it once here.
float playheadFeed = sampleAt(uPlayheadSeconds);
// ── EFFECT 2+3 geometry: evaluate the liquid SDF + its gradient (surface normal). ──
// The gradient of the SDF is the outward surface normal — we need it for the glass
// (specular, Fresnel, refraction). Central differences cost 4 extra field evals; the
// step is one device-pixel mapped into the field's xn/yTop frame.
vec2 px = vec2(screenX, screenYTop);
float amp;
float d = ribbonField(px, nowY, pixelsPerSecond, maxHalfWidth, amp);
float d = ribbonField(px, nowY, pixelsPerSecond, maxHalfWidth, playheadFeed, amp);
float e = 1.0; // 1px central-difference step
float ignore;
float dRx = ribbonField(px + vec2(e, 0.0), nowY, pixelsPerSecond, maxHalfWidth, ignore);
float dLx = ribbonField(px - vec2(e, 0.0), nowY, pixelsPerSecond, maxHalfWidth, ignore);
float dUy = ribbonField(px + vec2(0.0, e), nowY, pixelsPerSecond, maxHalfWidth, ignore);
float dDy = ribbonField(px - vec2(0.0, e), nowY, pixelsPerSecond, maxHalfWidth, ignore);
float dRx = ribbonField(px + vec2(e, 0.0), nowY, pixelsPerSecond, maxHalfWidth, playheadFeed, ignore);
float dLx = ribbonField(px - vec2(e, 0.0), nowY, pixelsPerSecond, maxHalfWidth, playheadFeed, ignore);
float dUy = ribbonField(px + vec2(0.0, e), nowY, pixelsPerSecond, maxHalfWidth, playheadFeed, ignore);
float dDy = ribbonField(px - vec2(0.0, e), nowY, pixelsPerSecond, maxHalfWidth, playheadFeed, ignore);
// Surface normal in screen space (points OUT of the liquid). y flipped because our
// field y is screen-down. Guard the zero-length case at flat interiors.
vec2 grad = vec2(dRx - dLx, dUy - dDy);