obs-StreamFX/data/effects/sdf/sdf-consumer.effect
Michael Fabian 'Xaymar' Dirks c59ec2511b filter-sdf-effects: Refactor to include Glow and Stroke (#2, #4)
This refactors the SDF Effects to use a normal blend function instead of doing the blend in the effect itself, improving quality and reducing problematic sampling issues. In addition to this, the effect files have been cleaned up slightly and renamed to their proper names. Glow and Stroke are now supported, which solves both #2 and #4 in one go.

The caching optimization has also now been implemented, reducing the number of renders for this filter to 1 for each tick.
2019-04-14 14:19:59 +02:00

294 lines
7.9 KiB
Text

// -------------------------------------------------------------------------------- //
// Defines
#define MAX_DISTANCE 65536.0
#define NEAR_INFINITE 18446744073709551616.0
#define FLT_SMALL 0.001
#define PI 3.1415926535897932384626433832795
#define HALFPI 1.5707963267948966192313216916398
// -------------------------------------------------------------------------------- //
// -------------------------------------------------------------------------------- //
// Samplers
sampler_state imageSampler {
Filter = Linear;
AddressU = Clamp;
AddressV = Clamp;
};
sampler_state sdfSampler {
Filter = Linear;
AddressU = Clamp;
AddressV = Clamp;
BorderColor = FFFFFFFF;
};
// -------------------------------------------------------------------------------- //
// -------------------------------------------------------------------------------- //
// Global Parameters
uniform float4x4 ViewProj;
uniform texture2d pSDFTexture;
uniform float pSDFThreshold;
uniform texture2d pImageTexture;
// -------------------------------------------------------------------------------- //
// -------------------------------------------------------------------------------- //
// Shared Functions
float4 PremultiplyTarget(float4 c, float4 t) {
return lerp(float4(t.r, t.g, t.b, c.a), c, c.a);
}
float LinearCurveFromValue(float v, float offset, float width,
float sharpness, float sharpnessInverse) {
}
float GradientFromValue(float v, float offset, float width) {
return ((v - offset) / width);
}
// We can use any of the following gradient functions: https://www.desmos.com/calculator/bmbrncaiem
float CurveEaseInOut(float v) {
return cos((v-1)*PI)*0.5+0.5;
}
float CurveEaseOut(float v) {
return sin(v * HALFPI);
}
float CurveEaseIn(float v) {
return 1 - cos(v * HALFPI);
}
// -------------------------------------------------------------------------------- //
// -------------------------------------------------------------------------------- //
// Default Vertex Shader
struct VertDataIn {
float4 pos : POSITION;
float2 uv : TEXCOORD0;
};
struct VertDataOut {
float4 pos : POSITION;
float2 uv : TEXCOORD0;
};
VertDataOut VSDefault(VertDataIn v_in)
{
VertDataOut vert_out;
vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj);
vert_out.uv = v_in.uv;
return vert_out;
}
// -------------------------------------------------------------------------------- //
// -------------------------------------------------------------------------------- //
// Shadow Effects (Inner, Outer)
uniform float4 pShadowColor;
uniform float pShadowMin;
uniform float pShadowMax;
uniform float2 pShadowOffset;
float4 PSShadowOuter(VertDataOut v_in) : TARGET
{
float2 dist_ex = pSDFTexture.Sample(sdfSampler, v_in.uv + pShadowOffset).rg * MAX_DISTANCE;
float dist = dist_ex.r - dist_ex.g;
bool mask = (pImageTexture.Sample(imageSampler, v_in.uv).a <= pSDFThreshold);
if (!mask) {
return float4(0.0, 0.0, 0.0, 0.0);
}
float v = clamp((dist - pShadowMin) / (pShadowMax - pShadowMin), 0., 1.);
return float4(pShadowColor.r, pShadowColor.g, pShadowColor.b, (1.0 - v) * pShadowColor.a);
}
technique ShadowOuter
{
pass
{
vertex_shader = VSDefault(v_in);
pixel_shader = PSShadowOuter(v_in);
}
}
float4 PSShadowInner(VertDataOut v_in) : TARGET
{
float2 dist_ex = pSDFTexture.Sample(sdfSampler, v_in.uv + pShadowOffset).rg * MAX_DISTANCE;
float dist = dist_ex.g - dist_ex.r;
bool mask = (pImageTexture.Sample(imageSampler, v_in.uv).a > pSDFThreshold);
if (!mask) {
return float4(0.0, 0.0, 0.0, 0.0);
}
float v = clamp((dist - pShadowMin) / (pShadowMax - pShadowMin), 0., 1.);
return float4(pShadowColor.r, pShadowColor.g, pShadowColor.b, (1.0 - v) * pShadowColor.a);
}
technique ShadowInner
{
pass
{
vertex_shader = VSDefault(v_in);
pixel_shader = PSShadowInner(v_in);
}
}
// -------------------------------------------------------------------------------- //
// -------------------------------------------------------------------------------- //
// Glow
uniform float4 pGlowColor <
string name = "Glow Color";
string group = "Glow";
>;
uniform float pGlowWidth <
string name = "Glow Width";
string group = "Glow";
float minimum = 0.01;
float maximum = 16.0;
float delta = 0.01;
float default = 1.0;
>;
uniform float pGlowSharpness <
string name = "Glow Sharpness";
string group = "Glow";
float minimum = 0.01;
float maximum = 100.0;
float delta = 0.01;
float default = 1.0;
string set_expr = "x / 100.0";
>;
uniform float pGlowSharpnessInverse <
string name = "Glow Sharpness Inverse";
string group = "Glow";
string value_expr = "1.0 / (1.0 - pGlowSharpness)";
bool visible = false;
bool enabled = false;
>;
float4 GlowShared(float dist) {
// Calculate correct gradient value and also take into account glow alpha to not delete information.
float v = clamp((GradientFromValue(dist, 0, pGlowWidth) - pGlowSharpness) * pGlowSharpnessInverse, 0.0, 1.0);
return float4(pGlowColor.r, pGlowColor.g, pGlowColor.b, pGlowColor.a * (1.0 - v));
}
float4 PSGlowOuter(VertDataOut v_in) : TARGET
{
float dist = pSDFTexture.Sample(sdfSampler, v_in.uv).r * MAX_DISTANCE;
bool mask = (pImageTexture.Sample(imageSampler, v_in.uv).a <= pSDFThreshold);
if (!mask) {
return float4(0.0, 0.0, 0.0, 0.0);
}
return GlowShared(dist);
}
technique GlowOuter
{
pass
{
vertex_shader = VSDefault(v_in);
pixel_shader = PSGlowOuter(v_in);
}
}
float4 PSGlowInner(VertDataOut v_in) : TARGET
{
float dist = pSDFTexture.Sample(sdfSampler, v_in.uv).g * MAX_DISTANCE;
bool mask = (pImageTexture.Sample(imageSampler, v_in.uv).a > pSDFThreshold);
if (!mask) {
return float4(0.0, 0.0, 0.0, 0.0);
}
return GlowShared(dist);
}
technique GlowInner
{
pass
{
vertex_shader = VSDefault(v_in);
pixel_shader = PSGlowInner(v_in);
}
}
// -------------------------------------------------------------------------------- //
// -------------------------------------------------------------------------------- //
// Outline
uniform float4 pOutlineColor <
string name = "Outline Color";
string group = "Outline";
>;
uniform float pOutlineWidth <
string name = "Outline Width";
string group = "Outline";
float minimum = 0.01;
float maximum = 16.0;
float delta = 0.01;
float default = 1.0;
>;
uniform float pOutlineOffset <
string name = "Outline Offset";
string group = "Outline";
float minimum = -16.0;
float maximum = 16.0;
float delta = 0.01;
float default = 0.0;
>;
uniform float pOutlineSharpness <
string name = "Outline Sharpness";
string group = "Outline";
float minimum = 0.01;
float maximum = 100.0;
float delta = 0.01;
float default = 1.0;
string set_expr = "x / 100.0";
>;
uniform float pOutlineSharpnessInverse <
string name = "Outline Sharpness Inverse";
string group = "Outline";
string value_expr = "1.0 / (1.0 - pOutlineSharpness)";
bool visible = false;
bool enabled = false;
>;
float4 PSOutline(VertDataOut v_in) : TARGET
{
// Sample Input and SDF
float4 tex_input = pImageTexture.Sample(imageSampler, v_in.uv);
float4 tex_sdf = pSDFTexture.Sample(sdfSampler, v_in.uv);
// Expand SDF range to full.
float2 sdf = tex_sdf.rg * float2(MAX_DISTANCE, MAX_DISTANCE);
// Calculate distance.
float dist = sdf.r - sdf.g;
// Calculate where we are in the outline.
// We can use any of the following gradient functions: https://www.desmos.com/calculator/bmbrncaiem
/// Base Curve
float n = clamp(abs(dist - pOutlineOffset) / pOutlineWidth, 0.0, 1.0);
/// Sharpness Curve
float y1 = clamp((n - pOutlineSharpness) * pOutlineSharpnessInverse, 0.0, 1.0);
float y2 = cos((y1 - 1) * PI) * 0.5 + 0.5;
float y3 = sin(y1 * (PI / 2));
float y4 = sin((y1 - 1) * (PI / 2)) + 1.0;
// Blend by Color.a so that our outline doesn't delete information.
float l = 1.0 - ((1.0 - y1) * pOutlineColor.a);
return lerp(pOutlineColor, tex_input, l);
}
technique Outline
{
pass
{
vertex_shader = VSDefault(v_in);
pixel_shader = PSOutline(v_in);
}
}
// -------------------------------------------------------------------------------- //