float4 shadeVideo(ShaderContext context) { if (drop <= 0) return context.sourceColor; float age = max(context.time - dropTime, 0.0); float2 aspect = float2(context.outputResolution.x / max(context.outputResolution.y, 1.0), 1.0); float2 fromDrop = (context.uv - center) * aspect; float radius = length(fromDrop); float2 c0 = (float2(0.0, 0.0) - center) * aspect; float2 c1 = (float2(1.0, 0.0) - center) * aspect; float2 c2 = (float2(0.0, 1.0) - center) * aspect; float2 c3 = (float2(1.0, 1.0) - center) * aspect; float maxRadius = max(max(length(c0), length(c1)), max(length(c2), length(c3))); float progress = saturate(age / max(speed, 0.001)); float waveRadius = progress * maxRadius; float distanceToWave = radius - waveRadius; float waveWidth = max(width * maxRadius, 0.0001); float ring = (1.0 - smoothstep(waveWidth, waveWidth * 1.65, abs(distanceToWave))) * exp(-progress * damping * 0.35); float crest = 1.0 - smoothstep(0.0, waveWidth * 0.35, abs(distanceToWave)); float ripple = ring * (distanceToWave >= 0.0 ? 1.0 : -0.55); float2 direction = radius > 0.0001 ? fromDrop / radius : float2(0.0, 0.0); float2 uvOffset = direction * (ripple * strength); uvOffset.x /= aspect.x; float2 refractedUv = clamp(context.uv - uvOffset, float2(0.0, 0.0), float2(1.0, 1.0)); float4 refracted = sampleVideo(refractedUv); float highlight = saturate(ring * 0.75 + crest * 0.55); refracted.rgb += highlight * 0.075; return saturate(refracted); }