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); }