From 7e4ab5cbd805108e0cc5652f7d347bc2d30959e2 Mon Sep 17 00:00:00 2001 From: Aiden Date: Tue, 5 May 2026 23:57:02 +1000 Subject: [PATCH] V1 text, needs improvements --- README.md | 4 +-- .../OpenGLComposite.cpp | 23 ++++++++++++++++- shaders/text-overlay/shader.json | 9 +++++++ shaders/text-overlay/shader.slang | 25 +++++++++++++------ 4 files changed, 50 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 809f01d..c056681 100644 --- a/README.md +++ b/README.md @@ -232,7 +232,7 @@ If your Windows runner stores the Blackmagic SDK outside the repo, configure `GP ## Still todo Audio -Fonts +improve text rendering genlock Logs anamorphic desqueeze @@ -240,6 +240,6 @@ solid color layer refactor, cleanup of source files display URL (Maybe clicakable) for control in the windows app (Not on the output) Sound shader as seperate .slang in shader package? -runtime date time +runtime date time UTC and offset from PCs internal clock Add a value control to the color wheels ![alt text](image.png) \ No newline at end of file diff --git a/apps/LoopThroughWithOpenGLCompositing/OpenGLComposite.cpp b/apps/LoopThroughWithOpenGLCompositing/OpenGLComposite.cpp index 4bea15e..7a03279 100644 --- a/apps/LoopThroughWithOpenGLCompositing/OpenGLComposite.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/OpenGLComposite.cpp @@ -205,6 +205,9 @@ std::vector BuildLocalSdf(const std::vector& alpha const float distance = std::sqrt(static_cast(bestDistanceSq)); const float signedDistance = (inside ? 1.0f : -1.0f) * distance; float normalized = 0.5f + signedDistance / static_cast(kTextSdfSpread * 2); + const unsigned char sourceAlpha = alpha[static_cast(y) * width + x]; + if (sourceAlpha > 0 && sourceAlpha < 255) + normalized = static_cast(sourceAlpha) / 255.0f; if (normalized < 0.0f) normalized = 0.0f; if (normalized > 1.0f) @@ -220,6 +223,24 @@ std::vector BuildLocalSdf(const std::vector& alpha return sdf; } +std::vector BuildTextCoverageTexture(const std::vector& alpha, unsigned width, unsigned height) +{ + std::vector coverage(static_cast(width) * height * 4, 0); + for (unsigned y = 0; y < height; ++y) + { + for (unsigned x = 0; x < width; ++x) + { + const unsigned char value = alpha[static_cast(y) * width + x]; + const std::size_t out = (static_cast(y) * width + x) * 4; + coverage[out + 0] = value; + coverage[out + 1] = value; + coverage[out + 2] = value; + coverage[out + 3] = value; + } + } + return coverage; +} + std::vector FlipTextTextureForShaderUv(const std::vector& pixels, unsigned width, unsigned height) { std::vector flipped(pixels.size(), 0); @@ -414,7 +435,7 @@ bool RasterizeTextSdf(const std::string& text, const std::filesystem::path& font alpha[static_cast(y) * kTextTextureWidth + x] = static_cast(luminance); } } - sdf = BuildLocalSdf(alpha, kTextTextureWidth, kTextTextureHeight); + sdf = BuildTextCoverageTexture(alpha, kTextTextureWidth, kTextTextureHeight); sdf = BlurTextSdf(sdf, kTextTextureWidth, kTextTextureHeight, kTextSdfBlurPasses); sdf = FlipTextTextureForShaderUv(sdf, kTextTextureWidth, kTextTextureHeight); WriteTextMaskDebugDump(text, alpha, sdf, kTextTextureWidth, kTextTextureHeight); diff --git a/shaders/text-overlay/shader.json b/shaders/text-overlay/shader.json index b90f4fa..e5b4e52 100644 --- a/shaders/text-overlay/shader.json +++ b/shaders/text-overlay/shader.json @@ -57,6 +57,15 @@ "min": 0.0, "max": 0.5, "step": 0.01 + }, + { + "id": "softness", + "label": "Softness", + "type": "float", + "default": 0.04, + "min": 0.0, + "max": 0.3, + "step": 0.01 } ] } diff --git a/shaders/text-overlay/shader.slang b/shaders/text-overlay/shader.slang index 98dc8c2..1f627cc 100644 --- a/shaders/text-overlay/shader.slang +++ b/shaders/text-overlay/shader.slang @@ -20,18 +20,27 @@ float4 shadeVideo(ShaderContext context) bool insideTextRect = textUv.x >= 0.0 && textUv.x <= 1.0 && textUv.y >= 0.0 && textUv.y <= 1.0; float mask = insideTextRect ? sampleTitleText(textUv) : 0.0; - float edge = 0.5; - float aa = max(fwidth(mask) * 1.5, 0.006); - float outlineAmount = min(outlineWidth * 0.25, 0.24); + float edge = 0.02; + float aa = max(fwidth(mask) * 1.5, 0.002); float fill = smoothstep(edge - aa, edge + aa, mask); - float outlineField = smoothstep(edge - outlineAmount - aa, edge - outlineAmount + aa, mask); - float outline = saturate(outlineField - fill); - float textAlpha = max(fill * fillColor.a, outline * outlineColor.a); + float shadowRadius = min((outlineWidth + softness) * 0.025, 0.018); + float shadow = 0.0; + if (shadowRadius > 0.0001) + { + shadow = max(shadow, sampleTitleText(textUv + float2(shadowRadius, shadowRadius))); + shadow = max(shadow, sampleTitleText(textUv + float2(-shadowRadius, shadowRadius))); + shadow = max(shadow, sampleTitleText(textUv + float2(shadowRadius, -shadowRadius))); + shadow = max(shadow, sampleTitleText(textUv + float2(-shadowRadius, -shadowRadius))); + } + shadow = smoothstep(edge - aa, edge + aa, shadow) * (0.35 + softness); + float outlineAlpha = saturate(shadow * (1.0 - fill)) * outlineColor.a; + float fillAlpha = fill * fillColor.a; + float textAlpha = max(fillAlpha, outlineAlpha); if (textAlpha <= 0.0001) return context.sourceColor; float4 base = context.sourceColor; - float4 outlineLayer = float4(outlineColor.rgb * outlineColor.a, outline * outlineColor.a); - float4 fillLayer = float4(fillColor.rgb * fillColor.a, fill * fillColor.a); + float4 outlineLayer = float4(outlineColor.rgb * outlineAlpha, outlineAlpha); + float4 fillLayer = float4(fillColor.rgb * fillAlpha, fillAlpha); return saturate(compositeOver(compositeOver(base, outlineLayer), fillLayer)); }