float onOff(float a, float b, float c, float framecount) { return step(c, sin((framecount * 0.001) + a * cos((framecount * 0.001) * b))); } float2 jumpy(float2 uv, float framecount) { float2 look = uv; float m = frac(framecount / 4.0); float dy = look.y - m; float window = 1.0 / (1.0 + 80.0 * dy * dy); look.x += 0.05 * sin(look.y * 10.0 + framecount) / 20.0 * onOff(4.0, 4.0, 0.3, framecount) * (0.5 + cos(framecount * 20.0)) * window; float vShift = (0.1 * wiggle) * 0.4 * onOff(2.0, 3.0, 0.9, framecount) * (sin(framecount) * sin(framecount * 20.0) + (0.5 + 0.1 * sin(framecount * 200.0) * cos(framecount))); look.y = frac(look.y - 0.01 * vShift); return look; } float2 circle(float start, float points, float point) { float rad = 6.28318530718 * (1.0 / points) * (point + start); return float2(-(.3 + rad), cos(rad)); } float3 rgb2yiq(float3 c) { return float3( 0.2989 * c.x + 0.5959 * c.y + 0.2115 * c.z, 0.5870 * c.x - 0.2744 * c.y - 0.5229 * c.z, 0.1140 * c.x - 0.3216 * c.y + 0.3114 * c.z ); } float3 yiq2rgb(float3 c) { return float3( 1.0 * c.x + 1.0 * c.y + 1.0 * c.z, 0.956 * c.x - 0.2720 * c.y - 1.1060 * c.z, 0.6210 * c.x - 0.6474 * c.y + 1.7046 * c.z ); } float noiseHash(float2 p) { return frac(sin(dot(p, float2(127.1, 311.7))) * 43758.5453123); } float grainScalar(float2 uv) { return frac(sin(dot(uv, float2(12.9898, 78.233))) * 43758.5453); } float3 animatedChromaGrain(float2 uv, float time, float2 outputResolution, float grainSize) { float safeGrainSize = max(grainSize, 0.001); float2 baseUv = uv * outputResolution * float2(0.85, 0.95) / safeGrainSize; float2 grainUv = floor(baseUv) + 0.5; float2 drift = float2(time * 19.7, time * 23.3); float r = grainScalar(grainUv + drift + float2(13.1, 71.7)); float g = grainScalar(grainUv * float2(1.03, 0.97) + drift * 1.11 + float2(47.2, 19.4)); float b = grainScalar(grainUv * float2(0.96, 1.05) + drift * 0.91 + float2(83.6, 53.8)); return float3(r, g, b) * 2.0 - 1.0; } float3 softBloom(float2 uv, float2 outputResolution, float radius) { float2 pixel = 1.0 / max(outputResolution, float2(1.0, 1.0)); float2 dx = float2(pixel.x * radius, 0.0); float2 dy = float2(0.0, pixel.y * radius); float3 sum = sampleVideo(frac(uv)).rgb * 0.28; sum += sampleVideo(frac(uv + dx)).rgb * 0.14; sum += sampleVideo(frac(uv - dx)).rgb * 0.14; sum += sampleVideo(frac(uv + dy)).rgb * 0.14; sum += sampleVideo(frac(uv - dy)).rgb * 0.14; sum += sampleVideo(frac(uv + dx + dy)).rgb * 0.075; sum += sampleVideo(frac(uv + dx - dy)).rgb * 0.075; sum += sampleVideo(frac(uv - dx + dy)).rgb * 0.075; sum += sampleVideo(frac(uv - dx - dy)).rgb * 0.075; return sum; } float3 blurVhs(float2 uv, float d, int sampleCount) { float3 sum = float3(0.0, 0.0, 0.0); float weight = 1.0 / max(float(sampleCount), 1.0); float start = 2.0 / max(float(sampleCount), 1.0); float2 pixelOffset = float2(d, 0.0); float2 scale = 0.66 * 8.0 * pixelOffset; for (int i = 0; i < 15; ++i) { if (i >= sampleCount) break; float2 offset = circle(start, float(sampleCount), float(i)) * scale; sum += sampleVideo(frac(uv + offset)).rgb * weight; } return sum; } float4 shadeVideo(ShaderContext context) { float2 uv = context.uv; float framecount = frac(context.time * wiggleSpeed / 7.0) * 7.0; int sampleCount = int(clamp(blurSamples, 3.0, 15.0) + 0.5); float d = 0.1 - round(frac(context.time / 3.0)) * 0.1; uv = jumpy(uv, framecount); float s = 0.0001 * -d + 0.0001 * wiggle * sin(context.time * wiggleSpeed); float e = min(0.30, pow(max(0.0, cos(uv.y * 4.0 + 0.3) - 0.75) * (s + 0.5), 3.0)) * 25.0; float r = 250.0 * (2.0 * s); uv.x += abs(r * pow(min(0.003, (-uv.y + (0.01 * frac(context.time / 5.0) * 5.0))) * 3.0, 2.0)) * wiggle; d = 0.051 + abs(sin(s / 4.0)); float c = max(0.0001, 0.002 * d) * smear; float3 yBlur = blurVhs(uv, c + c * uv.x, sampleCount); float y = rgb2yiq(yBlur).r; uv.x += 0.01 * d; c *= 6.0; float3 iBlur = blurVhs(uv, c, sampleCount); float i = rgb2yiq(iBlur).g; uv.x += 0.005 * d; c *= 2.5; float3 qBlur = blurVhs(uv, c, sampleCount); float q = rgb2yiq(qBlur).b; float3 color = yiq2rgb(float3(y, i, q)) - pow(s + e * 2.0, 3.0); float2 centered = context.uv * 2.0 - 1.0; centered.x *= context.outputResolution.x / max(context.outputResolution.y, 1.0); float2 aberrationOffset = centered * (aberrationAmount * 0.0015); float redAberration = sampleVideo(frac(context.uv + aberrationOffset)).r; float blueAberration = sampleVideo(frac(context.uv - aberrationOffset)).b; color.r = lerp(color.r, redAberration, 0.35); color.b = lerp(color.b, blueAberration, 0.35); float2 halationOffset = float2(0.0015, 0.0) * (1.0 + smear * 0.35); float3 halationSource = sampleVideo(frac(context.uv + halationOffset)).rgb * 0.4 + sampleVideo(frac(context.uv - halationOffset)).rgb * 0.4 + sampleVideo(frac(context.uv + halationOffset * 2.0)).rgb * 0.2; float halationLuma = dot(halationSource, float3(0.299, 0.587, 0.114)); float halationMask = smoothstep(0.45, 1.0, halationLuma) * halationAmount; color += halationSource * float3(1.0, 0.38, 0.24) * halationMask * 0.35; float3 bloomSource = softBloom(context.uv, context.outputResolution, 2.0 + smear * 2.5); float bloomLuma = dot(bloomSource, float3(0.299, 0.587, 0.114)); float bloomMask = smoothstep(0.32, 1.0, bloomLuma) * bloomAmount; color = lerp(color, bloomSource, bloomAmount * 0.18); color += bloomSource * float3(1.0, 0.96, 0.92) * bloomMask * 0.24; float3 speckle = animatedChromaGrain(context.uv, context.time, context.outputResolution, noiseSize); float luma = dot(color, float3(0.299, 0.587, 0.114)); float noiseMask = lerp(0.65, 1.0, 1.0 - saturate(luma)); float chunkiness = lerp(1.0, 2.4, saturate((noiseSize - 1.0) / 5.0)); float3 chromaNoise = float3(speckle.x * 1.2, speckle.y * 0.28, speckle.z * 1.35); color += chromaNoise * noiseAmount * noiseMask * chunkiness; color.rg = lerp(color.rg, float2(color.r, color.g) + speckle.xy * noiseAmount * 0.2 * chunkiness, 0.35); color.b = lerp(color.b, color.b + speckle.z * noiseAmount * 0.28 * chunkiness, 0.5); float3 grayscale = float3(luma, luma, luma); color = lerp(color, grayscale, fadeAmount * 0.18); color = color * (1.0 - fadeAmount * 0.08) + float3(0.055, 0.055, 0.065) * fadeAmount; color = lerp(color, softBloom(context.uv, context.outputResolution, 1.0 + smear), fadeAmount * 0.12); float vignetteBase = context.uv.x * (1.0 - context.uv.x) * context.uv.y * (1.0 - context.uv.y); float vignette = saturate(pow(vignetteBase * 16.0, 0.22)); color *= lerp(1.0 - vignetteAmount, 1.0, vignette); return float4(saturate(color), 1.0); }