From 0deef5597460b9e5a7585b1106bcf6a869ed6979 Mon Sep 17 00:00:00 2001 From: Aiden Date: Mon, 4 May 2026 13:20:12 +1000 Subject: [PATCH] Overlay shader --- shaders/waveform-overlay/0.png | Bin 0 -> 3123 bytes shaders/waveform-overlay/100.png | Bin 0 -> 3493 bytes shaders/waveform-overlay/25.png | Bin 0 -> 3154 bytes shaders/waveform-overlay/50.png | Bin 0 -> 3449 bytes shaders/waveform-overlay/75.png | Bin 0 -> 3160 bytes shaders/waveform-overlay/shader.json | 90 +++++++++++++++++++++-- shaders/waveform-overlay/shader.slang | 101 ++++++++++++++++++++++---- 7 files changed, 169 insertions(+), 22 deletions(-) create mode 100644 shaders/waveform-overlay/0.png create mode 100644 shaders/waveform-overlay/100.png create mode 100644 shaders/waveform-overlay/25.png create mode 100644 shaders/waveform-overlay/50.png create mode 100644 shaders/waveform-overlay/75.png diff --git a/shaders/waveform-overlay/0.png b/shaders/waveform-overlay/0.png new file mode 100644 index 0000000000000000000000000000000000000000..ea442ffb942cddace2d9675dc7d32832f6b35dfd GIT binary patch literal 3123 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 z zeT$0DA27GHn=ETIxpiZ+g=cLQYPsjbu1lv%{`%p;37Vh|E5kfyapdJ6*X38MMOz|l zoA7qr@LrKmiq}h4aj*A=m!IZ!F|KZ!5or0M#k=fVZCknIT%XO4*ih rBCqWsJ5`fbGRyO~eWmQ^bA>iazb)e_V^s-mzYka(A};V<_m}?yQWxb* literal 0 HcmV?d00001 diff --git a/shaders/waveform-overlay/shader.json b/shaders/waveform-overlay/shader.json index 877d70f..e97a1b3 100644 --- a/shaders/waveform-overlay/shader.json +++ b/shaders/waveform-overlay/shader.json @@ -4,21 +4,61 @@ "description": "Draws a lightweight luma waveform overlay along the bottom of the video.", "category": "Utility", "entryPoint": "shadeVideo", + "textures": [ + { + "id": "label0Texture", + "path": "0.png" + }, + { + "id": "label25Texture", + "path": "25.png" + }, + { + "id": "label50Texture", + "path": "50.png" + }, + { + "id": "label75Texture", + "path": "75.png" + }, + { + "id": "label100Texture", + "path": "100.png" + } + ], "parameters": [ { - "id": "overlayHeight", - "label": "Overlay Height", + "id": "overlayScale", + "label": "Overlay Scale", "type": "float", - "default": 0.32, + "default": 0.4, "min": 0.1, "max": 1.0, "step": 0.01 }, + { + "id": "overlayPosition", + "label": "Overlay Position", + "type": "vec2", + "default": [0.24, 0.76], + "min": [0.0, 0.0], + "max": [1.0, 1.0], + "step": [0.01, 0.01] + }, + { + "id": "overlayPadding", + "label": "Overlay Padding", + "type": "float", + "default": 0.08, + "min": 0.0, + "max": 0.25, + "step": 0.01 + }, { "id": "waveformOpacity", "label": "Waveform Opacity", "type": "float", - "default": 0.8, + "default": 0.75, "min": 0.0, "max": 1.0, "step": 0.01 @@ -27,7 +67,7 @@ "id": "backgroundOpacity", "label": "Background", "type": "float", - "default": 0.35, + "default": 0.75, "min": 0.0, "max": 1.0, "step": 0.01 @@ -36,16 +76,52 @@ "id": "lineThickness", "label": "Line Thickness", "type": "float", - "default": 2.0, + "default": 1.5, "min": 0.5, "max": 10.0, "step": 0.1 }, + { + "id": "gridOpacity", + "label": "Grid Opacity", + "type": "float", + "default": 1, + "min": 0.0, + "max": 1.0, + "step": 0.01 + }, + { + "id": "waveformSamples", + "label": "Waveform Samples", + "type": "float", + "default": 64.0, + "min": 8.0, + "max": 96.0, + "step": 1.0 + }, + { + "id": "waveformGain", + "label": "Waveform Gain", + "type": "float", + "default": 12.0, + "min": 1.0, + "max": 32.0, + "step": 0.5 + }, + { + "id": "waveformNoiseReduction", + "label": "Noise Reduction", + "type": "float", + "default": 0.08, + "min": 0.0, + "max": 0.6, + "step": 0.01 + }, { "id": "waveformColor", "label": "Waveform Color", "type": "color", - "default": [0.2, 1.0, 0.65, 1.0] + "default": [1.0, 1.0, 1.0, 1.0] } ] } diff --git a/shaders/waveform-overlay/shader.slang b/shaders/waveform-overlay/shader.slang index fa913e5..f2f2d88 100644 --- a/shaders/waveform-overlay/shader.slang +++ b/shaders/waveform-overlay/shader.slang @@ -1,24 +1,95 @@ +float insideUnit(float2 uv) +{ + return step(0.0, uv.x) * step(uv.x, 1.0) * step(0.0, uv.y) * step(uv.y, 1.0); +} + +float4 blendLabel(float4 base, float4 labelSample, float inside) +{ + float labelMask = saturate(dot(labelSample.rgb, float3(0.2126, 0.7152, 0.0722)) * labelSample.a * inside); + float3 screened = 1.0 - (1.0 - base.rgb) * (1.0 - labelSample.rgb); + return float4(lerp(base.rgb, screened, labelMask), max(base.a, labelMask)); +} + float4 shadeVideo(ShaderContext context) { float4 color = context.sourceColor; - float height = saturate(overlayHeight); - float overlayStart = 1.0 - height; - if (context.uv.y < overlayStart) + + float targetAspect = 16.0 / 9.0; + float resolutionAspect = max(context.outputResolution.x, 1.0) / max(context.outputResolution.y, 1.0); + float width = saturate(overlayScale); + float height = width * resolutionAspect / targetAspect; + float fitScale = min(1.0 / max(width, 0.001), 1.0 / max(height, 0.001)); + width *= min(fitScale, 1.0); + height *= min(fitScale, 1.0); + float2 halfSize = float2(width, height) * 0.5; + float2 center = clamp(saturate(overlayPosition), halfSize, float2(1.0) - halfSize); + float2 overlayMin = center - halfSize; + float2 overlayMax = center + halfSize; + + if (context.uv.x < overlayMin.x || context.uv.x > overlayMax.x || + context.uv.y < overlayMin.y || context.uv.y > overlayMax.y) return color; - float2 scopeUv = float2(context.uv.x, (context.uv.y - overlayStart) / max(height, 0.001)); - float luma = dot(sampleVideo(float2(context.uv.x, 0.5)).rgb, float3(0.2126, 0.7152, 0.0722)); - float targetY = 1.0 - saturate(luma); - float pixelThickness = max(lineThickness, 0.5) / max(context.outputResolution.y * height, 1.0); - float wave = 1.0 - smoothstep(pixelThickness, pixelThickness * 2.5, abs(scopeUv.y - targetY)); - - float grid = 0.0; - grid = max(grid, 1.0 - smoothstep(0.002, 0.006, abs(scopeUv.y - 0.25))); - grid = max(grid, 1.0 - smoothstep(0.002, 0.006, abs(scopeUv.y - 0.50))); - grid = max(grid, 1.0 - smoothstep(0.002, 0.006, abs(scopeUv.y - 0.75))); + float2 boxUv = (context.uv - overlayMin) / max(float2(width, height), float2(0.001)); + float2 pad = min(float2(saturate(overlayPadding)), float2(0.45)); + float2 innerUv = (boxUv - pad) / max(float2(1.0) - pad * 2.0, float2(0.001)); float3 bg = lerp(color.rgb, float3(0.0, 0.0, 0.0), saturate(backgroundOpacity)); - float3 withGrid = lerp(bg, float3(0.16, 0.22, 0.28), grid * 0.5); - float3 scoped = lerp(withGrid, waveformColor.rgb, wave * saturate(waveformOpacity) * waveformColor.a); + float labelHeight = min(max(pad.x * 0.95, 0.048), 0.12); + float labelWidth = labelHeight * height * max(context.outputResolution.y, 1.0) / max(width * max(context.outputResolution.x, 1.0), 0.001); + float labelX = max(pad.x * 0.5, labelWidth * 0.55); + float y0 = pad.y; + float y25 = pad.y + 0.25 * (1.0 - pad.y * 2.0); + float y50 = pad.y + 0.50 * (1.0 - pad.y * 2.0); + float y75 = pad.y + 0.75 * (1.0 - pad.y * 2.0); + float y100 = 1.0 - pad.y; + + float4 label = float4(0.0); + float2 labelUv100 = (boxUv - float2(labelX, y100)) / float2(labelWidth, labelHeight) + float2(0.5); + label = blendLabel(label, label100Texture.Sample(labelUv100), insideUnit(labelUv100)); + float2 labelUv75 = (boxUv - float2(labelX, y75)) / float2(labelWidth, labelHeight) + float2(0.5); + label = blendLabel(label, label75Texture.Sample(labelUv75), insideUnit(labelUv75)); + float2 labelUv50 = (boxUv - float2(labelX, y50)) / float2(labelWidth, labelHeight) + float2(0.5); + label = blendLabel(label, label50Texture.Sample(labelUv50), insideUnit(labelUv50)); + float2 labelUv25 = (boxUv - float2(labelX, y25)) / float2(labelWidth, labelHeight) + float2(0.5); + label = blendLabel(label, label25Texture.Sample(labelUv25), insideUnit(labelUv25)); + float2 labelUv0 = (boxUv - float2(labelX, y0)) / float2(labelWidth, labelHeight) + float2(0.5); + label = blendLabel(label, label0Texture.Sample(labelUv0), insideUnit(labelUv0)); + + if (innerUv.x < 0.0 || innerUv.x > 1.0 || innerUv.y < 0.0 || innerUv.y > 1.0) + return float4(lerp(bg, label.rgb, label.a), color.a); + + float pixelThickness = max(lineThickness, 0.5) / max(context.outputResolution.y * height * (1.0 - pad.y * 2.0), 1.0); + float requestedSamples = clamp(waveformSamples, 1.0, 96.0); + float density = 0.0; + + for (int sampleIndex = 0; sampleIndex < 96; sampleIndex++) + { + float samplePosition = float(sampleIndex); + if (samplePosition >= requestedSamples) + break; + + float sourceY = (samplePosition + 0.5) / requestedSamples; + float luma = dot(sampleVideo(float2(innerUv.x, sourceY)).rgb, float3(0.2126, 0.7152, 0.0722)); + float targetY = 1.0 - saturate(luma); + float sampleHit = 1.0 - smoothstep(pixelThickness, pixelThickness * 2.5, abs(innerUv.y - targetY)); + + density += sampleHit; + } + + float wave = saturate(density / requestedSamples * max(waveformGain, 0.0)); + float floor = saturate(waveformNoiseReduction); + wave = smoothstep(floor, 1.0, wave); + + float grid = 0.0; + grid = max(grid, 1.0 - smoothstep(0.002, 0.006, abs(innerUv.y - 0.00))); + grid = max(grid, 1.0 - smoothstep(0.002, 0.006, abs(innerUv.y - 0.25))); + grid = max(grid, 1.0 - smoothstep(0.002, 0.006, abs(innerUv.y - 0.50))); + grid = max(grid, 1.0 - smoothstep(0.002, 0.006, abs(innerUv.y - 0.75))); + grid = max(grid, 1.0 - smoothstep(0.002, 0.006, abs(innerUv.y - 1.00))); + + float3 withGrid = lerp(bg, float3(0.16, 0.22, 0.28), grid * saturate(gridOpacity)); + float3 withLabel = lerp(withGrid, label.rgb, label.a); + float3 scoped = lerp(withLabel, waveformColor.rgb, wave * saturate(waveformOpacity) * waveformColor.a); return float4(scoped, color.a); }