float luminance(float3 color) { return dot(color, float3(0.2126, 0.7152, 0.0722)); } float4 updateBackgroundModel(ShaderContext context) { float3 liveColor = context.sourceColor.rgb; if (context.feedbackAvailable <= 0) return float4(liveColor, 1.0); float3 previousBackground = sampleFeedback(context.uv).rgb; float rate = saturate(learnRate); float3 nextBackground = lerp(previousBackground, liveColor, rate); return float4(saturate(nextBackground), 1.0); } float4 displayBackgroundDifference(ShaderContext context) { // In the display pass, context.sourceColor is the same-frame background // model produced by updateBackgroundModel(). float3 backgroundModel = context.sourceColor.rgb; float3 liveColor = sampleLayerInput(context.uv).rgb; float3 delta = abs(liveColor - backgroundModel); float difference = max(delta.r, max(delta.g, delta.b)); float thresholdWidth = max(softness, 0.0001); float motionMask = smoothstep( differenceThreshold - thresholdWidth, differenceThreshold + thresholdWidth, difference); float3 baseColor = lerp(liveColor, backgroundModel, saturate(backgroundMix)); float3 overlayColor = overlayTint.rgb * max(luminance(liveColor), 0.15); float overlayAmount = motionMask * saturate(overlayOpacity) * overlayTint.a; float3 displayColor = lerp(baseColor, baseColor + overlayColor, overlayAmount); return float4(saturate(displayColor), 1.0); }