Greenscreen adjsutments
This commit is contained in:
@@ -248,4 +248,4 @@ If `SLANG_ROOT` is not set, the workflow falls back to the repo-local default un
|
||||
- compute shaders or a small 1x1 or nx1 RGBA16f render target for abritary data store
|
||||
- allow shaders to read other shaders data store based on name? or output over OSC
|
||||
- Mipmappong for shader declared textures
|
||||
- unwrap a fish eyelens and mirror it and map it to equirectangulr for environmnet map purposes
|
||||
- Multipass for shaders at request
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "greenscreen-key",
|
||||
"name": "Greenscreen Key",
|
||||
"description": "Keys out a green screen background and outputs transparent alpha for compositing.",
|
||||
"description": "Production-style green/blue screen keyer with matte refinement, despill, edge treatment, and debug views.",
|
||||
"category": "Keying",
|
||||
"entryPoint": "shadeVideo",
|
||||
"parameters": [
|
||||
@@ -16,7 +16,7 @@
|
||||
},
|
||||
{
|
||||
"id": "threshold",
|
||||
"label": "Threshold",
|
||||
"label": "Screen Gain",
|
||||
"type": "float",
|
||||
"default": 0.24,
|
||||
"min": 0.01,
|
||||
@@ -27,20 +27,29 @@
|
||||
"id": "softness",
|
||||
"label": "Softness",
|
||||
"type": "float",
|
||||
"default": 0.12,
|
||||
"default": 0.16,
|
||||
"min": 0.001,
|
||||
"max": 0.5,
|
||||
"step": 0.005
|
||||
},
|
||||
{
|
||||
"id": "edgeSoftness",
|
||||
"label": "Edge Softness",
|
||||
"id": "screenBalance",
|
||||
"label": "Screen Balance",
|
||||
"type": "float",
|
||||
"default": 0.08,
|
||||
"default": 0.5,
|
||||
"min": 0.0,
|
||||
"max": 0.4,
|
||||
"max": 1.0,
|
||||
"step": 0.005
|
||||
},
|
||||
{
|
||||
"id": "screenPreBlur",
|
||||
"label": "Screen PreBlur",
|
||||
"type": "float",
|
||||
"default": 1.0,
|
||||
"min": 0.0,
|
||||
"max": 8.0,
|
||||
"step": 0.1
|
||||
},
|
||||
{
|
||||
"id": "erodeDilate",
|
||||
"label": "Erode/Dilate",
|
||||
@@ -50,6 +59,51 @@
|
||||
"max": 0.3,
|
||||
"step": 0.005
|
||||
},
|
||||
{
|
||||
"id": "matteBlur",
|
||||
"label": "Matte Blur",
|
||||
"type": "float",
|
||||
"default": 1.25,
|
||||
"min": 0.0,
|
||||
"max": 6.0,
|
||||
"step": 0.1
|
||||
},
|
||||
{
|
||||
"id": "matteGamma",
|
||||
"label": "Matte Gamma",
|
||||
"type": "float",
|
||||
"default": 1.0,
|
||||
"min": 0.25,
|
||||
"max": 4.0,
|
||||
"step": 0.01
|
||||
},
|
||||
{
|
||||
"id": "matteContrast",
|
||||
"label": "Matte Contrast",
|
||||
"type": "float",
|
||||
"default": 1.0,
|
||||
"min": 0.25,
|
||||
"max": 4.0,
|
||||
"step": 0.01
|
||||
},
|
||||
{
|
||||
"id": "blackCleanup",
|
||||
"label": "Black Cleanup",
|
||||
"type": "float",
|
||||
"default": 0.0,
|
||||
"min": 0.0,
|
||||
"max": 1.0,
|
||||
"step": 0.005
|
||||
},
|
||||
{
|
||||
"id": "whiteCleanup",
|
||||
"label": "White Cleanup",
|
||||
"type": "float",
|
||||
"default": 0.0,
|
||||
"min": 0.0,
|
||||
"max": 1.0,
|
||||
"step": 0.005
|
||||
},
|
||||
{
|
||||
"id": "despill",
|
||||
"label": "Despill",
|
||||
@@ -60,14 +114,41 @@
|
||||
"step": 0.01
|
||||
},
|
||||
{
|
||||
"id": "edgeBoost",
|
||||
"label": "Edge Boost",
|
||||
"id": "despillBias",
|
||||
"label": "Despill Bias",
|
||||
"type": "float",
|
||||
"default": 0.08,
|
||||
"min": -0.2,
|
||||
"max": 0.3,
|
||||
"default": 0.0,
|
||||
"min": -0.5,
|
||||
"max": 0.5,
|
||||
"step": 0.005
|
||||
},
|
||||
{
|
||||
"id": "spillTint",
|
||||
"label": "Spill Tint",
|
||||
"type": "color",
|
||||
"default": [1.0, 1.0, 1.0, 1.0],
|
||||
"min": [0.0, 0.0, 0.0, 0.0],
|
||||
"max": [1.0, 1.0, 1.0, 1.0],
|
||||
"step": [0.01, 0.01, 0.01, 0.01]
|
||||
},
|
||||
{
|
||||
"id": "edgeRecover",
|
||||
"label": "Edge Recover",
|
||||
"type": "float",
|
||||
"default": 0.18,
|
||||
"min": 0.0,
|
||||
"max": 1.0,
|
||||
"step": 0.005
|
||||
},
|
||||
{
|
||||
"id": "edgeColor",
|
||||
"label": "Edge Color",
|
||||
"type": "color",
|
||||
"default": [1.0, 1.0, 1.0, 1.0],
|
||||
"min": [0.0, 0.0, 0.0, 0.0],
|
||||
"max": [1.0, 1.0, 1.0, 1.0],
|
||||
"step": [0.01, 0.01, 0.01, 0.01]
|
||||
},
|
||||
{
|
||||
"id": "clipBlack",
|
||||
"label": "Clip Black",
|
||||
@@ -85,6 +166,19 @@
|
||||
"min": 0.5,
|
||||
"max": 1.0,
|
||||
"step": 0.005
|
||||
},
|
||||
{
|
||||
"id": "viewMode",
|
||||
"label": "View",
|
||||
"type": "enum",
|
||||
"default": "composite",
|
||||
"options": [
|
||||
{ "value": "composite", "label": "Composite" },
|
||||
{ "value": "matte", "label": "Matte" },
|
||||
{ "value": "spill", "label": "Spill" },
|
||||
{ "value": "despill", "label": "Despill" },
|
||||
{ "value": "status", "label": "Status" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -9,31 +9,167 @@ 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);
|
||||
|
||||
float3 keyColor = safeNormalize(max(screenColor.rgb, float3(0.0001, 0.0001, 0.0001)));
|
||||
float3 sampleColor = safeNormalize(max(color, float3(0.0001, 0.0001, 0.0001)));
|
||||
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));
|
||||
|
||||
float chromaDistance = length(sampleColor - keyColor);
|
||||
float matteCenter = threshold - erodeDilate;
|
||||
float matteFeather = max(softness + edgeSoftness, 0.0005);
|
||||
float alpha = smoothstep(matteCenter - matteFeather, matteCenter + matteFeather, chromaDistance);
|
||||
|
||||
alpha = saturate((alpha - clipBlack) / max(clipWhite - clipBlack, 0.0001));
|
||||
alpha = saturate(alpha + edgeBoost);
|
||||
|
||||
float greenExcess = max(0.0, color.g - max(color.r, color.b));
|
||||
float spillReduction = greenExcess * despill;
|
||||
|
||||
float3 despilled = color;
|
||||
despilled.g = max(0.0, despilled.g - spillReduction);
|
||||
|
||||
float neutral = luma709(despilled);
|
||||
despilled.rb += spillReduction * 0.25;
|
||||
despilled = lerp(float3(neutral, neutral, neutral), despilled, 0.92);
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user