Overlay shader
This commit is contained in:
BIN
shaders/waveform-overlay/0.png
Normal file
BIN
shaders/waveform-overlay/0.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.0 KiB |
BIN
shaders/waveform-overlay/100.png
Normal file
BIN
shaders/waveform-overlay/100.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
BIN
shaders/waveform-overlay/25.png
Normal file
BIN
shaders/waveform-overlay/25.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
BIN
shaders/waveform-overlay/50.png
Normal file
BIN
shaders/waveform-overlay/50.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
BIN
shaders/waveform-overlay/75.png
Normal file
BIN
shaders/waveform-overlay/75.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
@@ -4,21 +4,61 @@
|
|||||||
"description": "Draws a lightweight luma waveform overlay along the bottom of the video.",
|
"description": "Draws a lightweight luma waveform overlay along the bottom of the video.",
|
||||||
"category": "Utility",
|
"category": "Utility",
|
||||||
"entryPoint": "shadeVideo",
|
"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": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"id": "overlayHeight",
|
"id": "overlayScale",
|
||||||
"label": "Overlay Height",
|
"label": "Overlay Scale",
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"default": 0.32,
|
"default": 0.4,
|
||||||
"min": 0.1,
|
"min": 0.1,
|
||||||
"max": 1.0,
|
"max": 1.0,
|
||||||
"step": 0.01
|
"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",
|
"id": "waveformOpacity",
|
||||||
"label": "Waveform Opacity",
|
"label": "Waveform Opacity",
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"default": 0.8,
|
"default": 0.75,
|
||||||
"min": 0.0,
|
"min": 0.0,
|
||||||
"max": 1.0,
|
"max": 1.0,
|
||||||
"step": 0.01
|
"step": 0.01
|
||||||
@@ -27,7 +67,7 @@
|
|||||||
"id": "backgroundOpacity",
|
"id": "backgroundOpacity",
|
||||||
"label": "Background",
|
"label": "Background",
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"default": 0.35,
|
"default": 0.75,
|
||||||
"min": 0.0,
|
"min": 0.0,
|
||||||
"max": 1.0,
|
"max": 1.0,
|
||||||
"step": 0.01
|
"step": 0.01
|
||||||
@@ -36,16 +76,52 @@
|
|||||||
"id": "lineThickness",
|
"id": "lineThickness",
|
||||||
"label": "Line Thickness",
|
"label": "Line Thickness",
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"default": 2.0,
|
"default": 1.5,
|
||||||
"min": 0.5,
|
"min": 0.5,
|
||||||
"max": 10.0,
|
"max": 10.0,
|
||||||
"step": 0.1
|
"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",
|
"id": "waveformColor",
|
||||||
"label": "Waveform Color",
|
"label": "Waveform Color",
|
||||||
"type": "color",
|
"type": "color",
|
||||||
"default": [0.2, 1.0, 0.65, 1.0]
|
"default": [1.0, 1.0, 1.0, 1.0]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 shadeVideo(ShaderContext context)
|
||||||
{
|
{
|
||||||
float4 color = context.sourceColor;
|
float4 color = context.sourceColor;
|
||||||
float height = saturate(overlayHeight);
|
|
||||||
float overlayStart = 1.0 - height;
|
float targetAspect = 16.0 / 9.0;
|
||||||
if (context.uv.y < overlayStart)
|
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;
|
return color;
|
||||||
|
|
||||||
float2 scopeUv = float2(context.uv.x, (context.uv.y - overlayStart) / max(height, 0.001));
|
float2 boxUv = (context.uv - overlayMin) / max(float2(width, height), float2(0.001));
|
||||||
float luma = dot(sampleVideo(float2(context.uv.x, 0.5)).rgb, float3(0.2126, 0.7152, 0.0722));
|
float2 pad = min(float2(saturate(overlayPadding)), float2(0.45));
|
||||||
float targetY = 1.0 - saturate(luma);
|
float2 innerUv = (boxUv - pad) / max(float2(1.0) - pad * 2.0, float2(0.001));
|
||||||
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)));
|
|
||||||
|
|
||||||
float3 bg = lerp(color.rgb, float3(0.0, 0.0, 0.0), saturate(backgroundOpacity));
|
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);
|
float labelHeight = min(max(pad.x * 0.95, 0.048), 0.12);
|
||||||
float3 scoped = lerp(withGrid, waveformColor.rgb, wave * saturate(waveformOpacity) * waveformColor.a);
|
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);
|
return float4(scoped, color.a);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user