Files
video-shader-toys/shaders/greenscreen-key/shader.slang
Aiden 6d5a606107
All checks were successful
CI / React UI Build (push) Successful in 10s
CI / Native Windows Build And Tests (push) Successful in 1m35s
CI / Windows Release Package (push) Successful in 2m22s
Greenscreen adjsutments
2026-05-08 16:11:43 +10:00

177 lines
6.5 KiB
Plaintext

float3 safeNormalize(float3 value)
{
float len = max(length(value), 0.0001);
return value / len;
}
float luma709(float3 color)
{
return dot(color, float3(0.2126, 0.7152, 0.0722));
}
float2 chroma709(float3 color)
{
float y = luma709(color);
return float2((color.b - y) * 0.5647, (color.r - y) * 0.7132);
}
float3 matteSampleColor(float2 uv, ShaderContext context)
{
float2 pixel = 1.0 / max(context.outputResolution, float2(1.0, 1.0));
float blur = max(screenPreBlur, 0.0);
float3 center = saturate(sampleVideo(saturate(uv)).rgb);
if (blur <= 0.0001)
return center;
float2 radius = pixel * blur;
float3 color = center * 0.36;
color += saturate(sampleVideo(saturate(uv + float2(radius.x, 0.0))).rgb) * 0.16;
color += saturate(sampleVideo(saturate(uv - float2(radius.x, 0.0))).rgb) * 0.16;
color += saturate(sampleVideo(saturate(uv + float2(0.0, radius.y))).rgb) * 0.16;
color += saturate(sampleVideo(saturate(uv - float2(0.0, radius.y))).rgb) * 0.16;
return color;
}
float keyDistanceAt(float2 uv, ShaderContext context)
{
float3 color = matteSampleColor(uv, context);
float3 keyColor = saturate(screenColor.rgb);
float chromaDistance = distance(chroma709(color), chroma709(keyColor)) * 2.65;
float directionDistance = length(safeNormalize(max(color, float3(0.0001, 0.0001, 0.0001))) - safeNormalize(max(keyColor, float3(0.0001, 0.0001, 0.0001)))) * 0.55;
return lerp(directionDistance, chromaDistance, saturate(screenBalance));
}
float rawAlphaAt(float2 uv, ShaderContext context)
{
float keyDistance = keyDistanceAt(uv, context);
float matteCenter = threshold + erodeDilate;
float matteFeather = max(softness, 0.0005);
float alpha = smoothstep(matteCenter - matteFeather, matteCenter + matteFeather, keyDistance);
return saturate(alpha);
}
float refinedAlphaAt(float2 uv, ShaderContext context)
{
float2 pixel = 1.0 / max(context.outputResolution, float2(1.0, 1.0));
float blur = max(matteBlur, 0.0);
float aaRadius = max(blur, 0.65);
float centerAlpha = rawAlphaAt(uv, context);
float alpha = centerAlpha * 0.30;
if (aaRadius > 0.0001)
{
float2 radius = pixel * aaRadius;
float2 halfRadius = radius * 0.5;
float alphaMin = centerAlpha;
float alphaMax = centerAlpha;
float sampleAlpha = rawAlphaAt(uv + float2(halfRadius.x, 0.0), context);
alpha += sampleAlpha * 0.065;
alphaMin = min(alphaMin, sampleAlpha);
alphaMax = max(alphaMax, sampleAlpha);
sampleAlpha = rawAlphaAt(uv - float2(halfRadius.x, 0.0), context);
alpha += sampleAlpha * 0.065;
alphaMin = min(alphaMin, sampleAlpha);
alphaMax = max(alphaMax, sampleAlpha);
sampleAlpha = rawAlphaAt(uv + float2(0.0, halfRadius.y), context);
alpha += sampleAlpha * 0.065;
alphaMin = min(alphaMin, sampleAlpha);
alphaMax = max(alphaMax, sampleAlpha);
sampleAlpha = rawAlphaAt(uv - float2(0.0, halfRadius.y), context);
alpha += sampleAlpha * 0.065;
alphaMin = min(alphaMin, sampleAlpha);
alphaMax = max(alphaMax, sampleAlpha);
sampleAlpha = rawAlphaAt(uv + float2(radius.x, 0.0), context);
alpha += sampleAlpha * 0.06;
alphaMin = min(alphaMin, sampleAlpha);
alphaMax = max(alphaMax, sampleAlpha);
sampleAlpha = rawAlphaAt(uv - float2(radius.x, 0.0), context);
alpha += sampleAlpha * 0.06;
alphaMin = min(alphaMin, sampleAlpha);
alphaMax = max(alphaMax, sampleAlpha);
sampleAlpha = rawAlphaAt(uv + float2(0.0, radius.y), context);
alpha += sampleAlpha * 0.06;
alphaMin = min(alphaMin, sampleAlpha);
alphaMax = max(alphaMax, sampleAlpha);
sampleAlpha = rawAlphaAt(uv - float2(0.0, radius.y), context);
alpha += sampleAlpha * 0.06;
alphaMin = min(alphaMin, sampleAlpha);
alphaMax = max(alphaMax, sampleAlpha);
sampleAlpha = rawAlphaAt(uv + radius, context);
alpha += sampleAlpha * 0.05;
alphaMin = min(alphaMin, sampleAlpha);
alphaMax = max(alphaMax, sampleAlpha);
sampleAlpha = rawAlphaAt(uv - radius, context);
alpha += sampleAlpha * 0.05;
alphaMin = min(alphaMin, sampleAlpha);
alphaMax = max(alphaMax, sampleAlpha);
sampleAlpha = rawAlphaAt(uv + float2(radius.x, -radius.y), context);
alpha += sampleAlpha * 0.05;
alphaMin = min(alphaMin, sampleAlpha);
alphaMax = max(alphaMax, sampleAlpha);
sampleAlpha = rawAlphaAt(uv + float2(-radius.x, radius.y), context);
alpha += sampleAlpha * 0.05;
alphaMin = min(alphaMin, sampleAlpha);
alphaMax = max(alphaMax, sampleAlpha);
alpha = lerp(alpha, alphaMin, saturate(blackCleanup));
alpha = lerp(alpha, alphaMax, saturate(whiteCleanup));
}
else
{
alpha = rawAlphaAt(uv, context);
}
alpha = saturate((alpha - clipBlack) / max(clipWhite - clipBlack, 0.0001));
alpha = saturate((alpha - 0.5) * max(matteContrast, 0.0001) + 0.5);
alpha = pow(max(alpha, 0.0), max(matteGamma, 0.0001));
return saturate(alpha);
}
float spillAmountForColor(float3 color)
{
float3 keyColor = saturate(screenColor.rgb);
float keyComponent = dot(color, safeNormalize(max(keyColor, float3(0.0001, 0.0001, 0.0001))));
float opposingComponent = max(max(color.r * (1.0 - keyColor.r), color.g * (1.0 - keyColor.g)), color.b * (1.0 - keyColor.b));
return saturate(keyComponent - opposingComponent + despillBias);
}
float3 despillColor(float3 color, float alpha)
{
float3 keyColor = safeNormalize(max(screenColor.rgb, float3(0.0001, 0.0001, 0.0001)));
float spill = spillAmountForColor(color) * despill * (1.0 - alpha * 0.35);
float neutral = luma709(color);
float3 neutralized = color - keyColor * spill;
neutralized = max(neutralized, float3(0.0, 0.0, 0.0));
neutralized = lerp(neutralized, float3(neutral, neutral, neutral), spill * 0.18);
neutralized = lerp(neutralized, neutralized * saturate(spillTint.rgb), saturate(spill));
return saturate(neutralized);
}
float4 shadeVideo(ShaderContext context)
{
float4 src = context.sourceColor;
float3 color = saturate(src.rgb);
float alpha = refinedAlphaAt(context.uv, context);
float spill = spillAmountForColor(color);
float3 despilled = despillColor(color, alpha);
float edgeAmount = saturate(1.0 - abs(alpha * 2.0 - 1.0));
despilled = lerp(despilled, despilled * saturate(edgeColor.rgb), edgeAmount * saturate(edgeRecover));
alpha = saturate(lerp(alpha, rawAlphaAt(context.uv, context), edgeAmount * saturate(edgeRecover) * 0.35));
if (viewMode == 1)
return float4(alpha, alpha, alpha, 1.0);
if (viewMode == 2)
return float4(spill, spill * 0.55, 0.0, 1.0);
if (viewMode == 3)
return float4(despilled, 1.0);
if (viewMode == 4)
{
float rawAlpha = rawAlphaAt(context.uv, context);
return float4(rawAlpha, alpha, spill, 1.0);
}
float3 premultiplied = saturate(despilled) * alpha;
return float4(premultiplied, alpha);
}