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.
This commit is contained in:
Michael Fabian 'Xaymar' Dirks 2019-04-06 19:37:26 +02:00 committed by Michael Fabian Dirks
parent 982dbc655b
commit c59ec2511b
7 changed files with 975 additions and 349 deletions

View file

@ -230,8 +230,6 @@ SET(PROJECT_DATA_EFFECTS
"${PROJECT_SOURCE_DIR}/data/effects/displace.effect" "${PROJECT_SOURCE_DIR}/data/effects/displace.effect"
"${PROJECT_SOURCE_DIR}/data/effects/mask.effect" "${PROJECT_SOURCE_DIR}/data/effects/mask.effect"
"${PROJECT_SOURCE_DIR}/data/effects/mipgen.effect" "${PROJECT_SOURCE_DIR}/data/effects/mipgen.effect"
"${PROJECT_SOURCE_DIR}/data/effects/sdf-generator.effect"
"${PROJECT_SOURCE_DIR}/data/effects/sdf-shadow.effect"
# Blur # Blur
"${PROJECT_SOURCE_DIR}/data/effects/blur/box.effect" "${PROJECT_SOURCE_DIR}/data/effects/blur/box.effect"
@ -239,6 +237,10 @@ SET(PROJECT_DATA_EFFECTS
"${PROJECT_SOURCE_DIR}/data/effects/blur/dual-filtering.effect" "${PROJECT_SOURCE_DIR}/data/effects/blur/dual-filtering.effect"
"${PROJECT_SOURCE_DIR}/data/effects/blur/gaussian.effect" "${PROJECT_SOURCE_DIR}/data/effects/blur/gaussian.effect"
"${PROJECT_SOURCE_DIR}/data/effects/blur/gaussian-linear.effect" "${PROJECT_SOURCE_DIR}/data/effects/blur/gaussian-linear.effect"
# Signed Distance Field
"${PROJECT_SOURCE_DIR}/data/effects/sdf/sdf-producer.effect"
"${PROJECT_SOURCE_DIR}/data/effects/sdf/sdf-consumer.effect"
) )
SET(PROJECT_DATA_SHADERS SET(PROJECT_DATA_SHADERS
# "${PROJECT_SOURCE_DIR}/data/shaders/name.effect" # "${PROJECT_SOURCE_DIR}/data/shaders/name.effect"

View file

@ -1,158 +0,0 @@
// -------------------------------------------------------------------------------- //
// Defines
#define MAX_DISTANCE 65536.0
#define NEAR_INFINITE 18446744073709551616.0
#define FLT_SMALL 0.001
// -------------------------------------------------------------------------------- //
// OBS Default
uniform float4x4 ViewProj;
// Inputs
uniform texture2d _sdf;
uniform texture2d _image;
uniform float _inner_min;
uniform float _inner_max;
uniform float2 _inner_offset;
uniform float4 _inner_color;
uniform float _outer_min;
uniform float _outer_max;
uniform float2 _outer_offset;
uniform float4 _outer_color;
uniform float _threshold;
sampler_state sdfSampler {
Filter = Linear;
AddressU = Clamp;
AddressV = Clamp;
};
sampler_state sdfSampler1_1 {
Filter = Linear;
AddressU = Border;
AddressV = Border;
BorderColor = 000000FF;
};
sampler_state imageSampler {
Filter = Linear;
AddressU = Clamp;
AddressV = Clamp;
};
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;
}
float4 PS_SDFShadow_v1(VertDataOut v_in) : TARGET
{
float4 final = _image.Sample(imageSampler, v_in.uv);
if (_inner_max > 0 && final.a >= _threshold) {
float inner_dist = _sdf.Sample(sdfSampler, v_in.uv + _inner_offset).g;
float range = (_inner_max - _inner_min);
float str = clamp(inner_dist - _inner_min, 0, range) / range;
final = lerp(_inner_color, final, str);
}
if (_outer_max > 0 && final.a < _threshold) {
float outer_dist = _sdf.Sample(sdfSampler, v_in.uv + _outer_offset).r;
float range = (_outer_max - _outer_min);
float str = clamp(outer_dist - _outer_min, 0, range) / range;
final = lerp(_outer_color, float4(_outer_color.r, _outer_color.g, _outer_color.b, 0.0), str);
}
return final;
}
float4 PS_SDFShadow_v1_1(VertDataOut v_in) : TARGET
{
// V1.1:
// - No longer clipping off image using _threshold.
// - Inverse Gradient supported (max < min)
// - Negative Gradient support (min/max < 0)
// Support for inverse Gradient (Outer Shadow): Maximum < Minimum
//
// Min=4.00, Max=0.00:
// Expected: Max is fully visible, Min and higher is fully shadowed.
// d=0.5
// d = d - 4.00 (= -3.5)
// d = d / 4.00 (= -0.875, 87.5%)
// d is 87.5% visible
// Normal:
// Min=0.00, Max=4.00:
// d=0.5
// d = d - 0.00 (= 0.5)
// d = d / 4.00 (= 0.125, 12.5%)
// d is 12.5% visible
float4 final = float4(0., 0., 0., 0.);
float4 base = _image.Sample(imageSampler, v_in.uv);
//! Outer Shadow
// Are we allowed to draw an outer shadow?
float2 outer_sdf = _sdf.Sample(sdfSampler1_1, v_in.uv + _outer_offset).rg * float2(MAX_DISTANCE, MAX_DISTANCE);
if ((_outer_color.a > 0.) && (outer_sdf.r < MAX_DISTANCE)) {
// Calculate the true distance value:
// - If we are outside, this will be positive.
// - If we are inside, this will be negative.
float sdf_distance = outer_sdf.r - outer_sdf.g;
// Calculate the delta.
float delta = _outer_max - _outer_min;
float t1 = sdf_distance - _outer_min;
float t2 = clamp(t1 / delta, 0., 1.);
final = lerp(final, _outer_color, (1. - t2) * _outer_color.a);
}
// Base Image
if (base.a > 0.) {
float3 rgb = base.rgb / base.a;
final = lerp(final, base, base.a);
}
//! Inner Shadow
// Are we allowed to draw an inner shadow?
float2 inner_sdf = _sdf.Sample(sdfSampler1_1, v_in.uv + _inner_offset).rg * float2(MAX_DISTANCE, MAX_DISTANCE);
if ((_inner_color.a > 0.) && (inner_sdf.g < MAX_DISTANCE) && (inner_sdf.r <= FLT_SMALL)) {
// Calculate the true distance value:
// - If we are outside, this will be positive.
// - If we are inside, this will be negative.
float sdf_distance = inner_sdf.g;
// Calculate the delta.
float delta = _inner_max - _inner_min;
float t1 = sdf_distance - _inner_min;
float t2 = clamp(t1 / delta, 0., 1.);
// Inner shadow does not affect alpha of image.
final.rgb = lerp(final.rgb, _inner_color.rgb, (1. - t2) * _inner_color.a);
}
return final;
}
technique Draw
{
pass
{
vertex_shader = VSDefault(v_in);
pixel_shader = PS_SDFShadow_v1_1(v_in);
}
}

View file

@ -0,0 +1,294 @@
// -------------------------------------------------------------------------------- //
// 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);
}
}
// -------------------------------------------------------------------------------- //

View file

@ -115,22 +115,56 @@ Filter.Displacement.Scale="Scale"
Filter.SDFEffects="Signed Distance Field Effects" Filter.SDFEffects="Signed Distance Field Effects"
Filter.SDFEffects.Shadow.Inner="Inner Shadow" Filter.SDFEffects.Shadow.Inner="Inner Shadow"
Filter.SDFEffects.Shadow.Inner.Description="Draw a shadow on the inside of the source?" Filter.SDFEffects.Shadow.Inner.Description="Draw a shadow on the inside of the source?"
Filter.SDFEffects.Shadow.Inner.Range.Minimum="Inner Minimum Distance" Filter.SDFEffects.Shadow.Inner.Range.Minimum="Inner Shadow Minimum Distance"
Filter.SDFEffects.Shadow.Inner.Range.Maximum="Inner Maximum Distance" Filter.SDFEffects.Shadow.Inner.Range.Maximum="Inner Shadow Maximum Distance"
Filter.SDFEffects.Shadow.Inner.Offset.X="Inner Offset X" Filter.SDFEffects.Shadow.Inner.Offset.X="Inner Shadow Offset X"
Filter.SDFEffects.Shadow.Inner.Offset.Y="Inner Offset Y" Filter.SDFEffects.Shadow.Inner.Offset.Y="Inner Shadow Offset Y"
Filter.SDFEffects.Shadow.Inner.Color="Inner Color" Filter.SDFEffects.Shadow.Inner.Color="Inner Shadow Color"
Filter.SDFEffects.Shadow.Inner.Alpha="Inner Alpha" Filter.SDFEffects.Shadow.Inner.Alpha="Inner Shadow Alpha"
Filter.SDFEffects.Shadow.Outer="Outer Shadow" Filter.SDFEffects.Shadow.Outer="Outer Shadow"
Filter.SDFEffects.Shadow.Outer.Description="Draw a shadow on the outside of the source?" Filter.SDFEffects.Shadow.Outer.Description="Draw a shadow on the outside of the source?"
Filter.SDFEffects.Shadow.Outer.Range.Minimum="Outer Minimum Distance" Filter.SDFEffects.Shadow.Outer.Range.Minimum="Outer Shadow Minimum Distance"
Filter.SDFEffects.Shadow.Outer.Range.Maximum="Outer Maximum Distance" Filter.SDFEffects.Shadow.Outer.Range.Maximum="Outer Shadow Maximum Distance"
Filter.SDFEffects.Shadow.Outer.Offset.X="Outer Offset X" Filter.SDFEffects.Shadow.Outer.Offset.X="Outer Shadow Offset X"
Filter.SDFEffects.Shadow.Outer.Offset.Y="Outer Offset Y" Filter.SDFEffects.Shadow.Outer.Offset.Y="Outer Shadow Offset Y"
Filter.SDFEffects.Shadow.Outer.Color="Outer Color" Filter.SDFEffects.Shadow.Outer.Color="Outer Shadow Color"
Filter.SDFEffects.Shadow.Outer.Alpha="Outer Alpha" Filter.SDFEffects.Shadow.Outer.Alpha="Outer Shadow Alpha"
Filter.SDFEffects.Glow.Outer="Outer Glow"
Filter.SDFEffects.Glow.Outer.Description="Draw an outline?"
Filter.SDFEffects.Glow.Outer.Color="Outer Glow Color"
Filter.SDFEffects.Glow.Outer.Color.Description="Color of the Glow."
Filter.SDFEffects.Glow.Outer.Alpha="Outer Glow Alpha"
Filter.SDFEffects.Glow.Outer.Alpha.Description="Alpha of the Glow."
Filter.SDFEffects.Glow.Outer.Width="Outer Glow Width"
Filter.SDFEffects.Glow.Outer.Width.Description="Size of the glow from the center line."
Filter.SDFEffects.Glow.Outer.Sharpness="Outer Glow Sharpness"
Filter.SDFEffects.Glow.Outer.Sharpness.Description="The sharpness of the glow in percent, with higher values being sharper."
Filter.SDFEffects.Glow.Inner="Inner Glow"
Filter.SDFEffects.Glow.Inner.Description="Draw an outline?"
Filter.SDFEffects.Glow.Inner.Color="Inner Glow Color"
Filter.SDFEffects.Glow.Inner.Color.Description="Color of the Glow."
Filter.SDFEffects.Glow.Inner.Alpha="Inner Glow Alpha"
Filter.SDFEffects.Glow.Inner.Alpha.Description="Alpha of the Glow."
Filter.SDFEffects.Glow.Inner.Width="Inner Glow Width"
Filter.SDFEffects.Glow.Inner.Width.Description="Size of the glow from the center."
Filter.SDFEffects.Glow.Inner.Sharpness="Inner Glow Sharpness"
Filter.SDFEffects.Glow.Inner.Sharpness.Description="The sharpness of the glow in percent, with higher values being sharper."
Filter.SDFEffects.Outline="Outline"
Filter.SDFEffects.Outline.Description="Draw an outline?"
Filter.SDFEffects.Outline.Color="Outline Color"
Filter.SDFEffects.Outline.Color.Description="Color of the Outline."
Filter.SDFEffects.Outline.Alpha="Outline Alpha"
Filter.SDFEffects.Outline.Alpha.Description="Alpha of the Outline."
Filter.SDFEffects.Outline.Width="Outline Width"
Filter.SDFEffects.Outline.Width.Description="Size of the outline in both direction from the center."
Filter.SDFEffects.Outline.Offset="Outline Offset"
Filter.SDFEffects.Outline.Offset.Description="Moves the center line by the given offset.\nA positive offset pushes the outline away from the source, while a negative one pulls it into the source."
Filter.SDFEffects.Outline.Sharpness="Outline Sharpness"
Filter.SDFEffects.Outline.Sharpness.Description="The sharpness of the outline in percent, with higher values being sharper."
Filter.SDFEffects.SDF.Scale="SDF Texture Scale" Filter.SDFEffects.SDF.Scale="SDF Texture Scale"
Filter.SDFEffects.SDF.Scale.Description="Percentage to scale the SDF Texture Size by, relative to the Source Size.\nA higher value results in better quality, but slower updates,\n while lower values result in faster updates, but lower quality." Filter.SDFEffects.SDF.Scale.Description="Percentage to scale the SDF Texture Size by, relative to the Source Size.\nA higher value results in better quality, but slower updates,\n while lower values result in faster updates, but lower quality."
Filter.SDFEffects.SDF.Threshold="SDF Alpha Threshold"
Filter.SDFEffects.SDF.Threshold.Description="Minimum opacity value in percent for SDF generation to consider the pixel solid."
# Filter - Transform # Filter - Transform
Filter.Transform="3D Transform" Filter.Transform="3D Transform"

View file

@ -18,8 +18,11 @@
*/ */
#include "filter-sdf-effects.hpp" #include "filter-sdf-effects.hpp"
#include "obs/gs/gs-helper.hpp"
#include "strings.hpp" #include "strings.hpp"
#define LOG_PREFIX "<filter-sdf-effects> "
// Translation Strings // Translation Strings
#define SOURCE_NAME "Filter.SDFEffects" #define SOURCE_NAME "Filter.SDFEffects"
@ -39,7 +42,27 @@
#define P_SHADOW_OUTER_COLOR "Filter.SDFEffects.Shadow.Outer.Color" #define P_SHADOW_OUTER_COLOR "Filter.SDFEffects.Shadow.Outer.Color"
#define P_SHADOW_OUTER_ALPHA "Filter.SDFEffects.Shadow.Outer.Alpha" #define P_SHADOW_OUTER_ALPHA "Filter.SDFEffects.Shadow.Outer.Alpha"
#define P_GLOW_INNER "Filter.SDFEffects.Glow.Inner"
#define P_GLOW_INNER_COLOR "Filter.SDFEffects.Glow.Inner.Color"
#define P_GLOW_INNER_ALPHA "Filter.SDFEffects.Glow.Inner.Alpha"
#define P_GLOW_INNER_WIDTH "Filter.SDFEffects.Glow.Inner.Width"
#define P_GLOW_INNER_SHARPNESS "Filter.SDFEffects.Glow.Inner.Sharpness"
#define P_GLOW_OUTER "Filter.SDFEffects.Glow.Outer"
#define P_GLOW_OUTER_COLOR "Filter.SDFEffects.Glow.Outer.Color"
#define P_GLOW_OUTER_ALPHA "Filter.SDFEffects.Glow.Outer.Alpha"
#define P_GLOW_OUTER_WIDTH "Filter.SDFEffects.Glow.Outer.Width"
#define P_GLOW_OUTER_SHARPNESS "Filter.SDFEffects.Glow.Outer.Sharpness"
#define P_OUTLINE "Filter.SDFEffects.Outline"
#define P_OUTLINE_COLOR "Filter.SDFEffects.Outline.Color"
#define P_OUTLINE_ALPHA "Filter.SDFEffects.Outline.Alpha"
#define P_OUTLINE_WIDTH "Filter.SDFEffects.Outline.Width"
#define P_OUTLINE_OFFSET "Filter.SDFEffects.Outline.Offset"
#define P_OUTLINE_SHARPNESS "Filter.SDFEffects.Outline.Sharpness"
#define P_SDF_SCALE "Filter.SDFEffects.SDF.Scale" #define P_SDF_SCALE "Filter.SDFEffects.SDF.Scale"
#define P_SDF_THRESHOLD "Filter.SDFEffects.SDF.Threshold"
// Initializer & Finalizer // Initializer & Finalizer
INITIALIZER(filterShadowFactoryInitializer) INITIALIZER(filterShadowFactoryInitializer)
@ -90,30 +113,32 @@ filter::sdf_effects::sdf_effects_factory::~sdf_effects_factory() {}
void filter::sdf_effects::sdf_effects_factory::on_list_fill() void filter::sdf_effects::sdf_effects_factory::on_list_fill()
{ {
{ auto gctx = gs::context();
char* file = obs_module_file("effects/sdf-generator.effect");
try { std::pair<const char*, std::shared_ptr<gs::effect>&> load_arr[] = {
sdf_generator_effect = std::make_shared<gs::effect>(file); {"effects/sdf/sdf-producer.effect", this->sdf_producer_effect},
} catch (std::runtime_error ex) { {"effects/sdf/sdf-consumer.effect", this->sdf_consumer_effect},
P_LOG_ERROR("<filter-shadow> Loading effect '%s' failed with error(s): %s", file, ex.what()); };
for (auto& kv : load_arr) {
char* path = obs_module_file(kv.first);
if (!path) {
P_LOG_ERROR(LOG_PREFIX "Unable to load effect '%s' as file is missing or locked.", kv.first);
continue;
} }
bfree(file);
}
{
char* file = obs_module_file("effects/sdf-shadow.effect");
try { try {
sdf_shadow_effect = std::make_shared<gs::effect>(file); kv.second = std::make_shared<gs::effect>(path);
} catch (std::runtime_error ex) { } catch (std::exception ex) {
P_LOG_ERROR("<filter-shadow> Loading effect '%s' failed with error(s): %s", file, ex.what()); P_LOG_ERROR(LOG_PREFIX "Failed to load effect '%s' (located at '%s') with error(s): %s", kv.first, path,
ex.what());
} }
bfree(file); bfree(path);
} }
} }
void filter::sdf_effects::sdf_effects_factory::on_list_empty() void filter::sdf_effects::sdf_effects_factory::on_list_empty()
{ {
sdf_generator_effect.reset(); this->sdf_producer_effect.reset();
sdf_shadow_effect.reset(); this->sdf_consumer_effect.reset();
} }
void* filter::sdf_effects::sdf_effects_factory::create(obs_data_t* data, obs_source_t* parent) void* filter::sdf_effects::sdf_effects_factory::create(obs_data_t* data, obs_source_t* parent)
@ -139,24 +164,44 @@ void filter::sdf_effects::sdf_effects_factory::destroy(void* inptr)
void filter::sdf_effects::sdf_effects_factory::get_defaults(obs_data_t* data) void filter::sdf_effects::sdf_effects_factory::get_defaults(obs_data_t* data)
{ {
obs_data_set_default_bool(data, P_SHADOW_INNER, false);
obs_data_set_default_double(data, P_SHADOW_INNER_RANGE_MINIMUM, 0.0);
obs_data_set_default_double(data, P_SHADOW_INNER_RANGE_MAXIMUM, 4.0);
obs_data_set_default_double(data, P_SHADOW_INNER_OFFSET_X, 0.0);
obs_data_set_default_double(data, P_SHADOW_INNER_OFFSET_Y, 0.0);
obs_data_set_default_int(data, P_SHADOW_INNER_COLOR, 0x00000000);
obs_data_set_default_double(data, P_SHADOW_INNER_ALPHA, 100.0);
obs_data_set_default_bool(data, P_SHADOW_OUTER, false); obs_data_set_default_bool(data, P_SHADOW_OUTER, false);
obs_data_set_default_int(data, P_SHADOW_OUTER_COLOR, 0x00000000);
obs_data_set_default_double(data, P_SHADOW_OUTER_ALPHA, 100.0);
obs_data_set_default_double(data, P_SHADOW_OUTER_RANGE_MINIMUM, 0.0); obs_data_set_default_double(data, P_SHADOW_OUTER_RANGE_MINIMUM, 0.0);
obs_data_set_default_double(data, P_SHADOW_OUTER_RANGE_MAXIMUM, 4.0); obs_data_set_default_double(data, P_SHADOW_OUTER_RANGE_MAXIMUM, 4.0);
obs_data_set_default_double(data, P_SHADOW_OUTER_OFFSET_X, 0.0); obs_data_set_default_double(data, P_SHADOW_OUTER_OFFSET_X, 0.0);
obs_data_set_default_double(data, P_SHADOW_OUTER_OFFSET_Y, 0.0); obs_data_set_default_double(data, P_SHADOW_OUTER_OFFSET_Y, 0.0);
obs_data_set_default_int(data, P_SHADOW_OUTER_COLOR, 0x00000000);
obs_data_set_default_double(data, P_SHADOW_OUTER_ALPHA, 100.0); obs_data_set_default_bool(data, P_SHADOW_INNER, false);
obs_data_set_default_int(data, P_SHADOW_INNER_COLOR, 0x00000000);
obs_data_set_default_double(data, P_SHADOW_INNER_ALPHA, 100.0);
obs_data_set_default_double(data, P_SHADOW_INNER_RANGE_MINIMUM, 0.0);
obs_data_set_default_double(data, P_SHADOW_INNER_RANGE_MAXIMUM, 4.0);
obs_data_set_default_double(data, P_SHADOW_INNER_OFFSET_X, 0.0);
obs_data_set_default_double(data, P_SHADOW_INNER_OFFSET_Y, 0.0);
obs_data_set_default_bool(data, P_GLOW_OUTER, false);
obs_data_set_default_int(data, P_GLOW_OUTER_COLOR, 0xFFFFFFFF);
obs_data_set_default_double(data, P_GLOW_OUTER_ALPHA, 100.0);
obs_data_set_default_double(data, P_GLOW_OUTER_WIDTH, 4.0);
obs_data_set_default_double(data, P_GLOW_OUTER_SHARPNESS, 50.0);
obs_data_set_default_bool(data, P_GLOW_INNER, false);
obs_data_set_default_int(data, P_GLOW_INNER_COLOR, 0xFFFFFFFF);
obs_data_set_default_double(data, P_GLOW_INNER_ALPHA, 100.0);
obs_data_set_default_double(data, P_GLOW_INNER_WIDTH, 4.0);
obs_data_set_default_double(data, P_GLOW_INNER_SHARPNESS, 50.0);
obs_data_set_default_bool(data, P_OUTLINE, false);
obs_data_set_default_int(data, P_OUTLINE_COLOR, 0x00000000);
obs_data_set_default_double(data, P_OUTLINE_ALPHA, 100.0);
obs_data_set_default_double(data, P_OUTLINE_WIDTH, 4.0);
obs_data_set_default_double(data, P_OUTLINE_OFFSET, 0.0);
obs_data_set_default_double(data, P_OUTLINE_SHARPNESS, 50.0);
obs_data_set_default_bool(data, S_ADVANCED, false); obs_data_set_default_bool(data, S_ADVANCED, false);
obs_data_set_default_double(data, P_SDF_SCALE, 100.0); obs_data_set_default_double(data, P_SDF_SCALE, 100.0);
obs_data_set_default_double(data, P_SDF_THRESHOLD, 50.0);
} }
obs_properties_t* filter::sdf_effects::sdf_effects_factory::get_properties(void* inptr) obs_properties_t* filter::sdf_effects::sdf_effects_factory::get_properties(void* inptr)
@ -204,18 +249,18 @@ void filter::sdf_effects::sdf_effects_factory::video_render(void* inptr, gs_effe
reinterpret_cast<filter::sdf_effects::sdf_effects_instance*>(inptr)->video_render(effect); reinterpret_cast<filter::sdf_effects::sdf_effects_instance*>(inptr)->video_render(effect);
} }
std::shared_ptr<gs::effect> filter::sdf_effects::sdf_effects_factory::get_sdf_generator_effect() std::shared_ptr<gs::effect> filter::sdf_effects::sdf_effects_factory::get_sdf_producer_effect()
{ {
return sdf_generator_effect; return this->sdf_producer_effect;
} }
std::shared_ptr<gs::effect> filter::sdf_effects::sdf_effects_factory::get_sdf_shadow_effect() std::shared_ptr<gs::effect> filter::sdf_effects::sdf_effects_factory::get_sdf_consumer_effect()
{ {
return sdf_shadow_effect; return this->sdf_consumer_effect;
} }
bool filter::sdf_effects::sdf_effects_instance::cb_modified_inside(void*, obs_properties_t* props, obs_property*, bool filter::sdf_effects::sdf_effects_instance::cb_modified_shadow_inside(void*, obs_properties_t* props, obs_property*,
obs_data_t* settings) obs_data_t* settings)
{ {
bool v = obs_data_get_bool(settings, P_SHADOW_INNER); bool v = obs_data_get_bool(settings, P_SHADOW_INNER);
obs_property_set_visible(obs_properties_get(props, P_SHADOW_INNER_RANGE_MINIMUM), v); obs_property_set_visible(obs_properties_get(props, P_SHADOW_INNER_RANGE_MINIMUM), v);
@ -227,8 +272,8 @@ bool filter::sdf_effects::sdf_effects_instance::cb_modified_inside(void*, obs_pr
return true; return true;
} }
bool filter::sdf_effects::sdf_effects_instance::cb_modified_outside(void*, obs_properties_t* props, obs_property*, bool filter::sdf_effects::sdf_effects_instance::cb_modified_shadow_outside(void*, obs_properties_t* props,
obs_data_t* settings) obs_property*, obs_data_t* settings)
{ {
bool v = obs_data_get_bool(settings, P_SHADOW_OUTER); bool v = obs_data_get_bool(settings, P_SHADOW_OUTER);
obs_property_set_visible(obs_properties_get(props, P_SHADOW_OUTER_RANGE_MINIMUM), v); obs_property_set_visible(obs_properties_get(props, P_SHADOW_OUTER_RANGE_MINIMUM), v);
@ -240,27 +285,69 @@ bool filter::sdf_effects::sdf_effects_instance::cb_modified_outside(void*, obs_p
return true; return true;
} }
bool filter::sdf_effects::sdf_effects_instance::cb_modified_advanced(void* , obs_properties_t* props, bool filter::sdf_effects::sdf_effects_instance::cb_modified_glow_inside(void*, obs_properties_t* props, obs_property*,
obs_property* , obs_data_t* settings) obs_data_t* settings)
{
bool v = obs_data_get_bool(settings, P_GLOW_INNER);
obs_property_set_visible(obs_properties_get(props, P_GLOW_INNER_COLOR), v);
obs_property_set_visible(obs_properties_get(props, P_GLOW_INNER_ALPHA), v);
obs_property_set_visible(obs_properties_get(props, P_GLOW_INNER_WIDTH), v);
obs_property_set_visible(obs_properties_get(props, P_GLOW_INNER_SHARPNESS), v);
return true;
}
bool filter::sdf_effects::sdf_effects_instance::cb_modified_glow_outside(void*, obs_properties_t* props, obs_property*,
obs_data_t* settings)
{
bool v = obs_data_get_bool(settings, P_GLOW_OUTER);
obs_property_set_visible(obs_properties_get(props, P_GLOW_OUTER_COLOR), v);
obs_property_set_visible(obs_properties_get(props, P_GLOW_OUTER_ALPHA), v);
obs_property_set_visible(obs_properties_get(props, P_GLOW_OUTER_WIDTH), v);
obs_property_set_visible(obs_properties_get(props, P_GLOW_OUTER_SHARPNESS), v);
return true;
}
bool filter::sdf_effects::sdf_effects_instance::cb_modified_outline(void*, obs_properties_t* props, obs_property*,
obs_data_t* settings)
{
bool v = obs_data_get_bool(settings, P_OUTLINE);
obs_property_set_visible(obs_properties_get(props, P_OUTLINE_COLOR), v);
obs_property_set_visible(obs_properties_get(props, P_OUTLINE_ALPHA), v);
obs_property_set_visible(obs_properties_get(props, P_OUTLINE_WIDTH), v);
obs_property_set_visible(obs_properties_get(props, P_OUTLINE_OFFSET), v);
obs_property_set_visible(obs_properties_get(props, P_OUTLINE_SHARPNESS), v);
return true;
}
bool filter::sdf_effects::sdf_effects_instance::cb_modified_advanced(void*, obs_properties_t* props, obs_property*,
obs_data_t* settings)
{ {
bool show_advanced = obs_data_get_bool(settings, S_ADVANCED); bool show_advanced = obs_data_get_bool(settings, S_ADVANCED);
obs_property_set_visible(obs_properties_get(props, P_SDF_SCALE), show_advanced); obs_property_set_visible(obs_properties_get(props, P_SDF_SCALE), show_advanced);
obs_property_set_visible(obs_properties_get(props, P_SDF_THRESHOLD), show_advanced);
return true; return true;
} }
filter::sdf_effects::sdf_effects_instance::sdf_effects_instance(obs_data_t* settings, obs_source_t* self) filter::sdf_effects::sdf_effects_instance::sdf_effects_instance(obs_data_t* settings, obs_source_t* self)
: m_self(self), m_source_rendered(false), m_sdf_scale(1.0) : m_self(self), m_source_rendered(false), m_sdf_scale(1.0)
{ {
this->m_source_rt = std::make_shared<gs::rendertarget>(GS_RGBA, GS_ZS_NONE);
this->m_sdf_write = std::make_shared<gs::rendertarget>(GS_RGBA32F, GS_ZS_NONE);
{ {
auto op = this->m_sdf_write->render(1, 1); auto gctx = gs::context();
vec4 transparent = {0, 0, 0, 0};
this->m_source_rt = std::make_shared<gs::rendertarget>(GS_RGBA, GS_ZS_NONE);
this->m_sdf_write = std::make_shared<gs::rendertarget>(GS_RGBA32F, GS_ZS_NONE);
this->m_sdf_read = std::make_shared<gs::rendertarget>(GS_RGBA32F, GS_ZS_NONE);
this->m_output_rt = std::make_shared<gs::rendertarget>(GS_RGBA, GS_ZS_NONE);
std::shared_ptr<gs::rendertarget> initialize_rts[] = {this->m_source_rt, this->m_sdf_write, this->m_sdf_read,
this->m_output_rt};
for (auto rt : initialize_rts) {
auto op = rt->render(1, 1);
gs_clear(GS_CLEAR_COLOR | GS_CLEAR_DEPTH, &transparent, 0, 0);
}
} }
this->m_sdf_read = std::make_shared<gs::rendertarget>(GS_RGBA32F, GS_ZS_NONE); update(settings);
{
auto op = this->m_sdf_read->render(1, 1);
}
this->update(settings);
} }
filter::sdf_effects::sdf_effects_instance::~sdf_effects_instance() {} filter::sdf_effects::sdf_effects_instance::~sdf_effects_instance() {}
@ -270,89 +357,253 @@ obs_properties_t* filter::sdf_effects::sdf_effects_instance::get_properties()
obs_properties_t* props = obs_properties_create(); obs_properties_t* props = obs_properties_create();
obs_property_t* p = nullptr; obs_property_t* p = nullptr;
p = obs_properties_add_bool(props, P_SHADOW_INNER, P_TRANSLATE(P_SHADOW_INNER)); {
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_INNER))); p = obs_properties_add_bool(props, P_SHADOW_OUTER, P_TRANSLATE(P_SHADOW_OUTER));
obs_property_set_modified_callback2(p, cb_modified_inside, this); obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_OUTER)));
obs_property_set_modified_callback2(p, cb_modified_shadow_outside, this);
p = obs_properties_add_float_slider(props, P_SHADOW_OUTER_RANGE_MINIMUM,
P_TRANSLATE(P_SHADOW_OUTER_RANGE_MINIMUM), -16.0, 16.0, 0.01);
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_OUTER_RANGE_MINIMUM)));
p = obs_properties_add_float_slider(props, P_SHADOW_OUTER_RANGE_MAXIMUM,
P_TRANSLATE(P_SHADOW_OUTER_RANGE_MAXIMUM), -16.0, 16.0, 0.01);
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_OUTER_RANGE_MAXIMUM)));
p = obs_properties_add_float_slider(props, P_SHADOW_OUTER_OFFSET_X, P_TRANSLATE(P_SHADOW_OUTER_OFFSET_X),
-100.0, 100.0, 0.01);
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_OUTER_OFFSET_X)));
p = obs_properties_add_float_slider(props, P_SHADOW_OUTER_OFFSET_Y, P_TRANSLATE(P_SHADOW_OUTER_OFFSET_Y),
-100.0, 100.0, 0.01);
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_OUTER_OFFSET_Y)));
p = obs_properties_add_color(props, P_SHADOW_OUTER_COLOR, P_TRANSLATE(P_SHADOW_OUTER_COLOR));
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_OUTER_COLOR)));
p = obs_properties_add_float_slider(props, P_SHADOW_OUTER_ALPHA, P_TRANSLATE(P_SHADOW_OUTER_ALPHA), 0.0, 100.0,
0.1);
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_OUTER_ALPHA)));
}
p = obs_properties_add_float_slider(props, P_SHADOW_INNER_RANGE_MINIMUM, P_TRANSLATE(P_SHADOW_INNER_RANGE_MINIMUM), {
-16.0, 16.0, 0.01); p = obs_properties_add_bool(props, P_SHADOW_INNER, P_TRANSLATE(P_SHADOW_INNER));
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_INNER_RANGE_MINIMUM))); obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_INNER)));
obs_property_set_modified_callback2(p, cb_modified_shadow_inside, this);
p = obs_properties_add_float_slider(props, P_SHADOW_INNER_RANGE_MINIMUM,
P_TRANSLATE(P_SHADOW_INNER_RANGE_MINIMUM), -16.0, 16.0, 0.01);
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_INNER_RANGE_MINIMUM)));
p = obs_properties_add_float_slider(props, P_SHADOW_INNER_RANGE_MAXIMUM,
P_TRANSLATE(P_SHADOW_INNER_RANGE_MAXIMUM), -16.0, 16.0, 0.01);
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_INNER_RANGE_MAXIMUM)));
p = obs_properties_add_float_slider(props, P_SHADOW_INNER_OFFSET_X, P_TRANSLATE(P_SHADOW_INNER_OFFSET_X),
-100.0, 100.0, 0.01);
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_INNER_OFFSET_X)));
p = obs_properties_add_float_slider(props, P_SHADOW_INNER_OFFSET_Y, P_TRANSLATE(P_SHADOW_INNER_OFFSET_Y),
-100.0, 100.0, 0.01);
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_INNER_OFFSET_Y)));
p = obs_properties_add_color(props, P_SHADOW_INNER_COLOR, P_TRANSLATE(P_SHADOW_INNER_COLOR));
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_INNER_COLOR)));
p = obs_properties_add_float_slider(props, P_SHADOW_INNER_ALPHA, P_TRANSLATE(P_SHADOW_INNER_ALPHA), 0.0, 100.0,
0.1);
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_INNER_ALPHA)));
}
p = obs_properties_add_float_slider(props, P_SHADOW_INNER_RANGE_MAXIMUM, P_TRANSLATE(P_SHADOW_INNER_RANGE_MAXIMUM), {
-16.0, 16.0, 0.01); p = obs_properties_add_bool(props, P_GLOW_OUTER, P_TRANSLATE(P_GLOW_OUTER));
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_INNER_RANGE_MAXIMUM))); obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_GLOW_OUTER)));
obs_property_set_modified_callback2(p, cb_modified_glow_outside, this);
p = obs_properties_add_float_slider(props, P_SHADOW_INNER_OFFSET_X, P_TRANSLATE(P_SHADOW_INNER_OFFSET_X), -100.0, p = obs_properties_add_color(props, P_GLOW_OUTER_COLOR, P_TRANSLATE(P_GLOW_OUTER_COLOR));
100.0, 0.01); obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_GLOW_OUTER_COLOR)));
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_INNER_OFFSET_X))); p = obs_properties_add_float_slider(props, P_GLOW_OUTER_ALPHA, P_TRANSLATE(P_GLOW_OUTER_ALPHA), 0.0, 100.0,
0.1);
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_GLOW_OUTER_ALPHA)));
p = obs_properties_add_float_slider(props, P_SHADOW_INNER_OFFSET_Y, P_TRANSLATE(P_SHADOW_INNER_OFFSET_Y), -100.0, obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_GLOW_OUTER_WIDTH)));
100.0, 0.01); p = obs_properties_add_float_slider(props, P_GLOW_OUTER_WIDTH, P_TRANSLATE(P_GLOW_OUTER_WIDTH), 0.0, 16.0,
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_INNER_OFFSET_Y))); 0.01);
p = obs_properties_add_color(props, P_SHADOW_INNER_COLOR, P_TRANSLATE(P_SHADOW_INNER_COLOR)); obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_GLOW_OUTER_SHARPNESS)));
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_INNER_COLOR))); p = obs_properties_add_float_slider(props, P_GLOW_OUTER_SHARPNESS, P_TRANSLATE(P_GLOW_OUTER_SHARPNESS), 0.00,
100.0, 0.01);
}
p = obs_properties_add_float_slider(props, P_SHADOW_INNER_ALPHA, P_TRANSLATE(P_SHADOW_INNER_ALPHA), 0.0, 100.0, {
0.1); p = obs_properties_add_bool(props, P_GLOW_INNER, P_TRANSLATE(P_GLOW_INNER));
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_INNER_ALPHA))); obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_GLOW_INNER)));
obs_property_set_modified_callback2(p, cb_modified_glow_inside, this);
p = obs_properties_add_bool(props, P_SHADOW_OUTER, P_TRANSLATE(P_SHADOW_OUTER)); p = obs_properties_add_color(props, P_GLOW_INNER_COLOR, P_TRANSLATE(P_GLOW_INNER_COLOR));
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_OUTER))); obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_GLOW_INNER_COLOR)));
obs_property_set_modified_callback2(p, cb_modified_outside, this); p = obs_properties_add_float_slider(props, P_GLOW_INNER_ALPHA, P_TRANSLATE(P_GLOW_INNER_ALPHA), 0.0, 100.0,
0.1);
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_GLOW_INNER_ALPHA)));
p = obs_properties_add_float_slider(props, P_SHADOW_OUTER_RANGE_MINIMUM, P_TRANSLATE(P_SHADOW_OUTER_RANGE_MINIMUM), obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_GLOW_INNER_WIDTH)));
-16.0, 16.0, 0.01); p = obs_properties_add_float_slider(props, P_GLOW_INNER_WIDTH, P_TRANSLATE(P_GLOW_INNER_WIDTH), 0.0, 16.0,
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_OUTER_RANGE_MINIMUM))); 0.01);
p = obs_properties_add_float_slider(props, P_SHADOW_OUTER_RANGE_MAXIMUM, P_TRANSLATE(P_SHADOW_OUTER_RANGE_MAXIMUM), obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_GLOW_INNER_SHARPNESS)));
-16.0, 16.0, 0.01); p = obs_properties_add_float_slider(props, P_GLOW_INNER_SHARPNESS, P_TRANSLATE(P_GLOW_INNER_SHARPNESS), 0.00,
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_OUTER_RANGE_MAXIMUM))); 100.0, 0.01);
}
p = obs_properties_add_float_slider(props, P_SHADOW_OUTER_OFFSET_X, P_TRANSLATE(P_SHADOW_OUTER_OFFSET_X), -100.0, {
100.0, 0.01); p = obs_properties_add_bool(props, P_OUTLINE, P_TRANSLATE(P_OUTLINE));
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_OUTER_OFFSET_X))); obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_OUTLINE)));
obs_property_set_modified_callback2(p, cb_modified_outline, this);
p = obs_properties_add_color(props, P_OUTLINE_COLOR, P_TRANSLATE(P_OUTLINE_COLOR));
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_OUTLINE_COLOR)));
p = obs_properties_add_float_slider(props, P_OUTLINE_ALPHA, P_TRANSLATE(P_OUTLINE_ALPHA), 0.0, 100.0, 0.1);
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_OUTLINE_ALPHA)));
p = obs_properties_add_float_slider(props, P_SHADOW_OUTER_OFFSET_Y, P_TRANSLATE(P_SHADOW_OUTER_OFFSET_Y), -100.0, obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_OUTLINE_WIDTH)));
100.0, 0.01); p = obs_properties_add_float_slider(props, P_OUTLINE_WIDTH, P_TRANSLATE(P_OUTLINE_WIDTH), 0.0, 16.0, 0.01);
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_OUTER_OFFSET_Y)));
p = obs_properties_add_color(props, P_SHADOW_OUTER_COLOR, P_TRANSLATE(P_SHADOW_OUTER_COLOR)); obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_OUTLINE_OFFSET)));
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_OUTER_COLOR))); p = obs_properties_add_float_slider(props, P_OUTLINE_OFFSET, P_TRANSLATE(P_OUTLINE_OFFSET), -16.0, 16.0, 0.01);
p = obs_properties_add_float_slider(props, P_SHADOW_OUTER_ALPHA, P_TRANSLATE(P_SHADOW_OUTER_ALPHA), 0.0, 100.0, obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_OUTLINE_SHARPNESS)));
0.1); p = obs_properties_add_float_slider(props, P_OUTLINE_SHARPNESS, P_TRANSLATE(P_OUTLINE_SHARPNESS), 0.00, 100.0,
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SHADOW_OUTER_ALPHA))); 0.01);
}
p = obs_properties_add_bool(props, S_ADVANCED, P_TRANSLATE(S_ADVANCED)); {
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(S_ADVANCED))); p = obs_properties_add_bool(props, S_ADVANCED, P_TRANSLATE(S_ADVANCED));
obs_property_set_modified_callback2(p, cb_modified_advanced, this); obs_property_set_long_description(p, P_TRANSLATE(P_DESC(S_ADVANCED)));
obs_property_set_modified_callback2(p, cb_modified_advanced, this);
p = obs_properties_add_float_slider(props, P_SDF_SCALE, P_TRANSLATE(P_SDF_SCALE), 0.1, 500.0, 0.1); p = obs_properties_add_float_slider(props, P_SDF_SCALE, P_TRANSLATE(P_SDF_SCALE), 0.1, 500.0, 0.1);
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SDF_SCALE))); obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SDF_SCALE)));
p = obs_properties_add_float_slider(props, P_SDF_THRESHOLD, P_TRANSLATE(P_SDF_THRESHOLD), 0.0, 100.0, 0.01);
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SDF_THRESHOLD)));
}
return props; return props;
} }
void filter::sdf_effects::sdf_effects_instance::update(obs_data_t* data) void filter::sdf_effects::sdf_effects_instance::update(obs_data_t* data)
{ {
this->m_inner_shadow = obs_data_get_bool(data, P_SHADOW_INNER); {
this->m_inner_range_min = float_t(obs_data_get_double(data, P_SHADOW_INNER_RANGE_MINIMUM)); this->m_outer_shadow =
this->m_inner_range_max = float_t(obs_data_get_double(data, P_SHADOW_INNER_RANGE_MAXIMUM)); obs_data_get_bool(data, P_SHADOW_OUTER) && (obs_data_get_double(data, P_SHADOW_OUTER_ALPHA) >= FLT_EPSILON);
this->m_inner_offset_x = float_t(obs_data_get_double(data, P_SHADOW_INNER_OFFSET_X)); {
this->m_inner_offset_y = float_t(obs_data_get_double(data, P_SHADOW_INNER_OFFSET_Y)); union {
this->m_inner_color = (obs_data_get_int(data, P_SHADOW_INNER_COLOR) & 0x00FFFFFF) uint32_t color;
| (int32_t(obs_data_get_double(data, P_SHADOW_INNER_ALPHA) * 2.55) << 24); uint8_t channel[4];
struct c {
uint8_t r, g, b, a;
} c;
};
color = uint32_t(obs_data_get_int(data, P_SHADOW_OUTER_COLOR));
this->m_outer_shadow_color.x = float_t(c.r / 255.0);
this->m_outer_shadow_color.y = float_t(c.g / 255.0);
this->m_outer_shadow_color.z = float_t(c.b / 255.0);
this->m_outer_shadow_color.w = float_t(obs_data_get_double(data, P_SHADOW_OUTER_ALPHA) / 100.0);
}
this->m_outer_shadow_range_min = float_t(obs_data_get_double(data, P_SHADOW_OUTER_RANGE_MINIMUM));
this->m_outer_shadow_range_max = float_t(obs_data_get_double(data, P_SHADOW_OUTER_RANGE_MAXIMUM));
this->m_outer_shadow_offset_x = float_t(obs_data_get_double(data, P_SHADOW_OUTER_OFFSET_X));
this->m_outer_shadow_offset_y = float_t(obs_data_get_double(data, P_SHADOW_OUTER_OFFSET_Y));
}
this->m_outer_shadow = obs_data_get_bool(data, P_SHADOW_OUTER); {
this->m_outer_range_min = float_t(obs_data_get_double(data, P_SHADOW_OUTER_RANGE_MINIMUM)); this->m_inner_shadow =
this->m_outer_range_max = float_t(obs_data_get_double(data, P_SHADOW_OUTER_RANGE_MAXIMUM)); obs_data_get_bool(data, P_SHADOW_INNER) && (obs_data_get_double(data, P_SHADOW_INNER_ALPHA) >= FLT_EPSILON);
this->m_outer_offset_x = float_t(obs_data_get_double(data, P_SHADOW_OUTER_OFFSET_X)); {
this->m_outer_offset_y = float_t(obs_data_get_double(data, P_SHADOW_OUTER_OFFSET_Y)); union {
this->m_outer_color = (obs_data_get_int(data, P_SHADOW_OUTER_COLOR) & 0x00FFFFFF) uint32_t color;
| (int32_t(obs_data_get_double(data, P_SHADOW_OUTER_ALPHA) * 2.55) << 24); uint8_t channel[4];
struct c {
uint8_t r, g, b, a;
} c;
};
color = uint32_t(obs_data_get_int(data, P_SHADOW_INNER_COLOR));
this->m_inner_shadow_color.x = float_t(c.r / 255.0);
this->m_inner_shadow_color.y = float_t(c.g / 255.0);
this->m_inner_shadow_color.z = float_t(c.b / 255.0);
this->m_inner_shadow_color.w = float_t(obs_data_get_double(data, P_SHADOW_INNER_ALPHA) / 100.0);
}
this->m_inner_shadow_range_min = float_t(obs_data_get_double(data, P_SHADOW_INNER_RANGE_MINIMUM));
this->m_inner_shadow_range_max = float_t(obs_data_get_double(data, P_SHADOW_INNER_RANGE_MAXIMUM));
this->m_inner_shadow_offset_x = float_t(obs_data_get_double(data, P_SHADOW_INNER_OFFSET_X));
this->m_inner_shadow_offset_y = float_t(obs_data_get_double(data, P_SHADOW_INNER_OFFSET_Y));
}
this->m_sdf_scale = double_t(obs_data_get_double(data, P_SDF_SCALE) / 100.0); {
this->m_outer_glow =
obs_data_get_bool(data, P_GLOW_OUTER) && (obs_data_get_double(data, P_GLOW_OUTER_ALPHA) >= FLT_EPSILON);
{
union {
uint32_t color;
uint8_t channel[4];
struct c {
uint8_t r, g, b, a;
} c;
};
color = uint32_t(obs_data_get_int(data, P_GLOW_OUTER_COLOR));
this->m_outer_glow_color.x = float_t(c.r / 255.0);
this->m_outer_glow_color.y = float_t(c.g / 255.0);
this->m_outer_glow_color.z = float_t(c.b / 255.0);
this->m_outer_glow_color.w = float_t(obs_data_get_double(data, P_GLOW_OUTER_ALPHA) / 100.0);
}
this->m_outer_glow_width = float_t(obs_data_get_double(data, P_GLOW_OUTER_WIDTH));
this->m_outer_glow_sharpness = float_t(obs_data_get_double(data, P_GLOW_OUTER_SHARPNESS) / 100.0);
this->m_outer_glow_sharpness_inv = float_t(1.0f / (1.0 - this->m_outer_glow_sharpness));
if (this->m_outer_glow_sharpness >= (1.0 - FLT_EPSILON)) {
this->m_outer_glow_sharpness = 1.0 - FLT_EPSILON;
}
}
{
this->m_inner_glow =
obs_data_get_bool(data, P_GLOW_INNER) && (obs_data_get_double(data, P_GLOW_INNER_ALPHA) >= FLT_EPSILON);
{
union {
uint32_t color;
uint8_t channel[4];
struct c {
uint8_t r, g, b, a;
} c;
};
color = uint32_t(obs_data_get_int(data, P_GLOW_INNER_COLOR));
this->m_inner_glow_color.x = float_t(c.r / 255.0);
this->m_inner_glow_color.y = float_t(c.g / 255.0);
this->m_inner_glow_color.z = float_t(c.b / 255.0);
this->m_inner_glow_color.w = float_t(obs_data_get_double(data, P_GLOW_INNER_ALPHA) / 100.0);
}
this->m_inner_glow_width = float_t(obs_data_get_double(data, P_GLOW_INNER_WIDTH));
this->m_inner_glow_sharpness = float_t(obs_data_get_double(data, P_GLOW_INNER_SHARPNESS) / 100.0);
this->m_inner_glow_sharpness_inv = float_t(1.0f / (1.0 - this->m_inner_glow_sharpness));
if (this->m_inner_glow_sharpness >= (1.0 - FLT_EPSILON)) {
this->m_inner_glow_sharpness = 1.0 - FLT_EPSILON;
}
}
{
this->m_outline =
obs_data_get_bool(data, P_OUTLINE) && (obs_data_get_double(data, P_OUTLINE_ALPHA) >= FLT_EPSILON);
{
union {
uint32_t color;
uint8_t channel[4];
struct c {
uint8_t r, g, b, a;
} c;
};
color = uint32_t(obs_data_get_int(data, P_OUTLINE_COLOR));
this->m_outline_color.x = float_t(c.r / 255.0);
this->m_outline_color.y = float_t(c.g / 255.0);
this->m_outline_color.z = float_t(c.b / 255.0);
this->m_outline_color.w = float_t(obs_data_get_double(data, P_OUTLINE_ALPHA) / 100.0);
}
this->m_outline_width = float_t(obs_data_get_double(data, P_OUTLINE_WIDTH));
this->m_outline_offset = float_t(obs_data_get_double(data, P_OUTLINE_OFFSET));
this->m_outline_sharpness = float_t(obs_data_get_double(data, P_OUTLINE_SHARPNESS) / 100.0);
this->m_outline_sharpness_inv = float_t(1.0f / (1.0 - this->m_outline_sharpness));
if (this->m_outline_sharpness >= (1.0 - FLT_EPSILON)) {
this->m_outline_sharpness = 1.0 - FLT_EPSILON;
}
}
this->m_sdf_scale = double_t(obs_data_get_double(data, P_SDF_SCALE) / 100.0);
this->m_sdf_threshold = float_t(obs_data_get_double(data, P_SDF_THRESHOLD) / 100.0);
} }
uint32_t filter::sdf_effects::sdf_effects_instance::get_width() uint32_t filter::sdf_effects::sdf_effects_instance::get_width()
@ -369,26 +620,45 @@ void filter::sdf_effects::sdf_effects_instance::activate() {}
void filter::sdf_effects::sdf_effects_instance::deactivate() {} void filter::sdf_effects::sdf_effects_instance::deactivate() {}
void filter::sdf_effects::sdf_effects_instance::video_tick(float ) void filter::sdf_effects::sdf_effects_instance::video_tick(float)
{ {
m_source_rendered = false; uint32_t width = 1;
uint32_t height = 1;
// Figure out the actual source size.
do {
obs_source_t* target = obs_filter_get_target(this->m_self);
if (target == nullptr) {
break;
}
// Grab width an height of the target source (child filter or source).
width = obs_source_get_width(target);
height = obs_source_get_height(target);
} while (false);
this->m_source_rendered = false;
this->m_output_rendered = false;
} }
void filter::sdf_effects::sdf_effects_instance::video_render(gs_effect_t*) void filter::sdf_effects::sdf_effects_instance::video_render(gs_effect_t* effect)
{ {
obs_source_t* parent = obs_filter_get_parent(this->m_self); obs_source_t* parent = obs_filter_get_parent(this->m_self);
obs_source_t* target = obs_filter_get_target(this->m_self); obs_source_t* target = obs_filter_get_target(this->m_self);
uint32_t baseW = obs_source_get_base_width(target); uint32_t baseW = obs_source_get_base_width(target);
uint32_t baseH = obs_source_get_base_height(target); uint32_t baseH = obs_source_get_base_height(target);
gs_effect_t* final_effect = effect ? effect : obs_get_base_effect(obs_base_effect::OBS_EFFECT_DEFAULT);
gs_effect_t* default_effect = obs_get_base_effect(obs_base_effect::OBS_EFFECT_DEFAULT); gs_effect_t* default_effect = obs_get_base_effect(obs_base_effect::OBS_EFFECT_DEFAULT);
vec4 color_transparent;
vec4_zero(&color_transparent);
if (!parent || !target || (baseW == 0) || (baseH == 0)) { if (!this->m_self || !parent || !target || !baseW || !baseH || !final_effect) {
obs_source_skip_video_filter(this->m_self); obs_source_skip_video_filter(this->m_self);
return; return;
} }
auto gctx = gs::context();
vec4 color_transparent = {0};
vec4_zero(&color_transparent);
try { try {
gs_blend_state_push(); gs_blend_state_push();
gs_reset_blend_state(); gs_reset_blend_state();
@ -412,12 +682,12 @@ void filter::sdf_effects::sdf_effects_instance::video_render(gs_effect_t*)
gs_clear(GS_CLEAR_COLOR | GS_CLEAR_DEPTH, &color_transparent, 0, 0); gs_clear(GS_CLEAR_COLOR | GS_CLEAR_DEPTH, &color_transparent, 0, 0);
if (obs_source_process_filter_begin(this->m_self, GS_RGBA, OBS_ALLOW_DIRECT_RENDERING)) { if (obs_source_process_filter_begin(this->m_self, GS_RGBA, OBS_ALLOW_DIRECT_RENDERING)) {
obs_source_process_filter_end(this->m_self, default_effect, baseW, baseH); obs_source_process_filter_end(this->m_self, final_effect, baseW, baseH);
} else { } else {
throw std::runtime_error("failed to process source"); throw std::runtime_error("failed to process source");
} }
} }
m_source_rt->get_texture(this->m_source_texture); this->m_source_rt->get_texture(this->m_source_texture);
if (!this->m_source_texture) { if (!this->m_source_texture) {
throw std::runtime_error("failed to draw source"); throw std::runtime_error("failed to draw source");
} }
@ -430,7 +700,7 @@ void filter::sdf_effects::sdf_effects_instance::video_render(gs_effect_t*)
} }
std::shared_ptr<gs::effect> sdf_effect = std::shared_ptr<gs::effect> sdf_effect =
filter::sdf_effects::sdf_effects_factory::get()->get_sdf_generator_effect(); filter::sdf_effects::sdf_effects_factory::get()->get_sdf_producer_effect();
if (!sdf_effect) { if (!sdf_effect) {
throw std::runtime_error("SDF Effect no loaded"); throw std::runtime_error("SDF Effect no loaded");
} }
@ -447,25 +717,27 @@ void filter::sdf_effects::sdf_effects_instance::video_render(gs_effect_t*)
} }
{ {
auto op = m_sdf_write->render(uint32_t(sdfW), uint32_t(sdfH)); auto op = this->m_sdf_write->render(uint32_t(sdfW), uint32_t(sdfH));
gs_ortho(0, (float)sdfW, 0, (float)sdfH, -1, 1); gs_ortho(0, (float)sdfW, 0, (float)sdfH, -1, 1);
gs_clear(GS_CLEAR_COLOR | GS_CLEAR_DEPTH, &color_transparent, 0, 0); gs_clear(GS_CLEAR_COLOR | GS_CLEAR_DEPTH, &color_transparent, 0, 0);
sdf_effect->get_parameter("_image").set_texture(this->m_source_texture); sdf_effect->get_parameter("_image").set_texture(this->m_source_texture);
sdf_effect->get_parameter("_size").set_float2(float_t(sdfW), float_t(sdfH)); sdf_effect->get_parameter("_size").set_float2(float_t(sdfW), float_t(sdfH));
sdf_effect->get_parameter("_sdf").set_texture(this->m_sdf_texture); sdf_effect->get_parameter("_sdf").set_texture(this->m_sdf_texture);
sdf_effect->get_parameter("_threshold").set_float(0.5); sdf_effect->get_parameter("_threshold").set_float(this->m_sdf_threshold);
while (gs_effect_loop(sdf_effect->get_object(), "Draw")) { while (gs_effect_loop(sdf_effect->get_object(), "Draw")) {
gs_draw_sprite(this->m_sdf_texture->get_object(), 0, uint32_t(sdfW), uint32_t(sdfH)); gs_draw_sprite(this->m_sdf_texture->get_object(), 0, uint32_t(sdfW), uint32_t(sdfH));
} }
} }
this->m_sdf_write.swap(this->m_sdf_read); std::swap(this->m_sdf_read, this->m_sdf_write);
this->m_sdf_read->get_texture(this->m_sdf_texture); this->m_sdf_read->get_texture(this->m_sdf_texture);
if (!this->m_sdf_texture) { if (!this->m_sdf_texture) {
throw std::runtime_error("SDF Backbuffer empty"); throw std::runtime_error("SDF Backbuffer empty");
} }
} }
this->m_source_rendered = true;
} }
gs_blend_state_pop(); gs_blend_state_pop();
@ -475,53 +747,200 @@ void filter::sdf_effects::sdf_effects_instance::video_render(gs_effect_t*)
return; return;
} }
try { if (!this->m_output_rendered) {
std::shared_ptr<gs::effect> shadow_effect = this->m_output_texture = this->m_source_texture;
filter::sdf_effects::sdf_effects_factory::get()->get_sdf_shadow_effect();
if (!shadow_effect) { std::shared_ptr<gs::effect> consumer_effect =
throw std::runtime_error("Shadow Effect no loaded"); filter::sdf_effects::sdf_effects_factory::get()->get_sdf_consumer_effect();
if (!consumer_effect) {
obs_source_skip_video_filter(this->m_self);
return;
} }
shadow_effect->get_parameter("_sdf").set_texture(this->m_sdf_texture); gs_blend_state_push();
shadow_effect->get_parameter("_image").set_texture(this->m_source_texture); gs_reset_blend_state();
shadow_effect->get_parameter("_threshold").set_float(0.5f); gs_enable_color(true, true, true, true);
gs_enable_depth_test(false);
gs_set_cull_mode(GS_NEITHER);
if (this->m_inner_shadow) { // SDF Effects Stack:
shadow_effect->get_parameter("_inner_min").set_float(this->m_inner_range_min); // Normal Source
shadow_effect->get_parameter("_inner_max").set_float(this->m_inner_range_max); // Outer Shadow
shadow_effect->get_parameter("_inner_offset") // Inner Shadow
.set_float2(this->m_inner_offset_x / float_t(baseW), this->m_inner_offset_y / float_t(baseH)); // Outer Glow
shadow_effect->get_parameter("_inner_color") // Inner Glow
.set_float4((this->m_inner_color & 0xFF) / 255.0f, ((this->m_inner_color >> 8) & 0xFF) / 255.0f, // Outline
((this->m_inner_color >> 16) & 0xFF) / 255.0f,
((this->m_inner_color >> 24) & 0xFF) / 255.0f); { // Normal Source
} else { gs_enable_blending(false);
shadow_effect->get_parameter("_inner_min").set_float(0.); gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
shadow_effect->get_parameter("_inner_max").set_float(0.);
shadow_effect->get_parameter("_inner_offset").set_float2(0., 0.); auto param = gs_effect_get_param_by_name(default_effect, "image");
shadow_effect->get_parameter("_inner_color").set_float4(0., 0., 0., 0.); if (param) {
} gs_effect_set_texture(param, this->m_output_texture->get_object());
if (this->m_outer_shadow) { }
shadow_effect->get_parameter("_outer_min").set_float(this->m_outer_range_min);
shadow_effect->get_parameter("_outer_max").set_float(this->m_outer_range_max); {
shadow_effect->get_parameter("_outer_offset") auto op = this->m_output_rt->render(baseW, baseH);
.set_float2(this->m_outer_offset_x / float_t(baseW), this->m_outer_offset_y / float_t(baseH)); gs_ortho(0, 1, 0, 1, 0, 1);
shadow_effect->get_parameter("_outer_color") while (gs_effect_loop(default_effect, "Draw")) {
.set_float4((this->m_outer_color & 0xFF) / 255.0f, ((this->m_outer_color >> 8) & 0xFF) / 255.0f, gs_draw_sprite(0, 0, 1, 1);
((this->m_outer_color >> 16) & 0xFF) / 255.0f, }
((this->m_outer_color >> 24) & 0xFF) / 255.0f); }
} else {
shadow_effect->get_parameter("_outer_min").set_float(0.); // Flip Buffers
shadow_effect->get_parameter("_outer_max").set_float(0.); m_output_rt->get_texture(this->m_output_texture);
shadow_effect->get_parameter("_outer_offset").set_float2(0., 0.);
shadow_effect->get_parameter("_outer_color").set_float4(0., 0., 0., 0.);
} }
while (gs_effect_loop(shadow_effect->get_object(), "Draw")) { if (m_outer_shadow) {
gs_draw_sprite(this->m_source_texture->get_object(), 0, baseW, baseH); try {
gs_enable_blending(true);
gs_blend_function_separate(GS_BLEND_SRCALPHA, GS_BLEND_DSTALPHA, GS_BLEND_ONE, GS_BLEND_ONE);
consumer_effect->get_parameter("pSDFTexture").set_texture(this->m_sdf_texture);
consumer_effect->get_parameter("pSDFThreshold").set_float(this->m_sdf_threshold);
consumer_effect->get_parameter("pImageTexture").set_texture(this->m_source_texture->get_object());
consumer_effect->get_parameter("pShadowColor").set_float4(this->m_outer_shadow_color);
consumer_effect->get_parameter("pShadowMin").set_float(this->m_outer_shadow_range_min);
consumer_effect->get_parameter("pShadowMax").set_float(this->m_outer_shadow_range_max);
consumer_effect->get_parameter("pShadowOffset")
.set_float2(this->m_outer_shadow_offset_x / float_t(baseW),
this->m_outer_shadow_offset_y / float_t(baseH));
{
auto op = this->m_output_rt->render(baseW, baseH);
gs_ortho(0, 1, 0, 1, 0, 1);
while (gs_effect_loop(consumer_effect->get_object(), "ShadowOuter")) {
gs_draw_sprite(0, 0, 1, 1);
}
}
// Flip Buffers
m_output_rt->get_texture(this->m_output_texture);
} catch (...) {
}
} }
} catch (...) {
obs_source_skip_video_filter(this->m_self); if (m_inner_shadow) {
return; try {
gs_enable_blending(true);
gs_blend_function_separate(GS_BLEND_SRCALPHA, GS_BLEND_INVSRCALPHA, GS_BLEND_ONE, GS_BLEND_ONE);
consumer_effect->get_parameter("pSDFTexture").set_texture(this->m_sdf_texture);
consumer_effect->get_parameter("pSDFThreshold").set_float(this->m_sdf_threshold);
consumer_effect->get_parameter("pImageTexture").set_texture(this->m_source_texture->get_object());
consumer_effect->get_parameter("pShadowColor").set_float4(this->m_inner_shadow_color);
consumer_effect->get_parameter("pShadowMin").set_float(this->m_inner_shadow_range_min);
consumer_effect->get_parameter("pShadowMax").set_float(this->m_inner_shadow_range_max);
consumer_effect->get_parameter("pShadowOffset")
.set_float2(this->m_inner_shadow_offset_x / float_t(baseW),
this->m_inner_shadow_offset_y / float_t(baseH));
{
auto op = this->m_output_rt->render(baseW, baseH);
gs_ortho(0, 1, 0, 1, 0, 1);
while (gs_effect_loop(consumer_effect->get_object(), "ShadowInner")) {
gs_draw_sprite(0, 0, 1, 1);
}
}
// Flip Buffers
m_output_rt->get_texture(this->m_output_texture);
} catch (...) {
}
}
if (m_outer_glow) {
try {
gs_enable_blending(true);
gs_blend_function_separate(GS_BLEND_SRCALPHA, GS_BLEND_DSTALPHA, GS_BLEND_ONE, GS_BLEND_ONE);
consumer_effect->get_parameter("pSDFTexture").set_texture(this->m_sdf_texture);
consumer_effect->get_parameter("pSDFThreshold").set_float(this->m_sdf_threshold);
consumer_effect->get_parameter("pImageTexture").set_texture(this->m_source_texture->get_object());
consumer_effect->get_parameter("pGlowColor").set_float4(this->m_outer_glow_color);
consumer_effect->get_parameter("pGlowWidth").set_float(this->m_outer_glow_width);
consumer_effect->get_parameter("pGlowSharpness").set_float(this->m_outer_glow_sharpness);
consumer_effect->get_parameter("pGlowSharpnessInverse").set_float(this->m_outer_glow_sharpness_inv);
{
auto op = this->m_output_rt->render(baseW, baseH);
gs_ortho(0, 1, 0, 1, 0, 1);
while (gs_effect_loop(consumer_effect->get_object(), "GlowOuter")) {
gs_draw_sprite(0, 0, 1, 1);
}
}
// Flip Buffers
m_output_rt->get_texture(this->m_output_texture);
} catch (...) {
}
}
if (m_inner_glow) {
try {
gs_enable_blending(true);
gs_blend_function_separate(GS_BLEND_SRCALPHA, GS_BLEND_INVSRCALPHA, GS_BLEND_ONE, GS_BLEND_ONE);
consumer_effect->get_parameter("pSDFTexture").set_texture(this->m_sdf_texture);
consumer_effect->get_parameter("pSDFThreshold").set_float(this->m_sdf_threshold);
consumer_effect->get_parameter("pImageTexture").set_texture(this->m_source_texture->get_object());
consumer_effect->get_parameter("pGlowColor").set_float4(this->m_inner_glow_color);
consumer_effect->get_parameter("pGlowWidth").set_float(this->m_inner_glow_width);
consumer_effect->get_parameter("pGlowSharpness").set_float(this->m_inner_glow_sharpness);
consumer_effect->get_parameter("pGlowSharpnessInverse").set_float(this->m_inner_glow_sharpness_inv);
{
auto op = this->m_output_rt->render(baseW, baseH);
gs_ortho(0, 1, 0, 1, 0, 1);
while (gs_effect_loop(consumer_effect->get_object(), "GlowInner")) {
gs_draw_sprite(0, 0, 1, 1);
}
}
// Flip Buffers
m_output_rt->get_texture(this->m_output_texture);
} catch (...) {
}
}
if (m_outline) {
try {
gs_enable_blending(true);
gs_blend_function_separate(GS_BLEND_SRCALPHA, GS_BLEND_INVSRCALPHA, GS_BLEND_ONE, GS_BLEND_ONE);
consumer_effect->get_parameter("pSDFTexture").set_texture(this->m_sdf_texture);
consumer_effect->get_parameter("pSDFThreshold").set_float(this->m_sdf_threshold);
consumer_effect->get_parameter("pImageTexture").set_texture(this->m_source_texture->get_object());
consumer_effect->get_parameter("pOutlineColor").set_float4(this->m_outline_color);
consumer_effect->get_parameter("pOutlineWidth").set_float(this->m_outline_width);
consumer_effect->get_parameter("pOutlineOffset").set_float(this->m_outline_offset);
consumer_effect->get_parameter("pOutlineSharpness").set_float(this->m_outline_sharpness);
consumer_effect->get_parameter("pOutlineSharpnessInverse").set_float(this->m_outline_sharpness_inv);
{
auto op = this->m_output_rt->render(baseW, baseH);
gs_ortho(0, 1, 0, 1, 0, 1);
while (gs_effect_loop(consumer_effect->get_object(), "Outline")) {
gs_draw_sprite(0, 0, 1, 1);
}
}
// Flip Buffers
m_output_rt->get_texture(this->m_output_texture);
} catch (...) {
}
}
gs_blend_state_pop();
this->m_output_rendered = true;
}
gs_eparam_t* ep = gs_effect_get_param_by_name(final_effect, "image");
if (ep) {
gs_effect_set_texture(ep, this->m_output_texture->get_object());
}
while (gs_effect_loop(final_effect, "Draw")) {
gs_draw_sprite(0, 0, baseW, baseH);
} }
} }

View file

@ -47,8 +47,8 @@ namespace filter {
std::list<sdf_effects_instance*> sources; std::list<sdf_effects_instance*> sources;
std::shared_ptr<gs::effect> sdf_generator_effect; std::shared_ptr<gs::effect> sdf_producer_effect;
std::shared_ptr<gs::effect> sdf_shadow_effect; std::shared_ptr<gs::effect> sdf_consumer_effect;
public: // Singleton public: // Singleton
static void initialize(); static void initialize();
@ -80,8 +80,8 @@ namespace filter {
static void video_render(void* source, gs_effect_t* effect); static void video_render(void* source, gs_effect_t* effect);
public: public:
std::shared_ptr<gs::effect> get_sdf_generator_effect(); std::shared_ptr<gs::effect> get_sdf_producer_effect();
std::shared_ptr<gs::effect> get_sdf_shadow_effect(); std::shared_ptr<gs::effect> get_sdf_consumer_effect();
}; };
class sdf_effects_instance { class sdf_effects_instance {
@ -96,24 +96,59 @@ namespace filter {
std::shared_ptr<gs::rendertarget> m_sdf_write, m_sdf_read; std::shared_ptr<gs::rendertarget> m_sdf_write, m_sdf_read;
std::shared_ptr<gs::texture> m_sdf_texture; std::shared_ptr<gs::texture> m_sdf_texture;
double_t m_sdf_scale; double_t m_sdf_scale;
float_t m_sdf_threshold;
bool m_inner_shadow; // Effects
float_t m_inner_range_min; bool m_output_rendered;
float_t m_inner_range_max; std::shared_ptr<gs::texture> m_output_texture;
float_t m_inner_offset_x; std::shared_ptr<gs::rendertarget> m_output_rt;
float_t m_inner_offset_y; /// Inner Shadow
uint32_t m_inner_color; bool m_inner_shadow;
bool m_outer_shadow; vec4 m_inner_shadow_color;
float_t m_outer_range_min; float_t m_inner_shadow_range_min;
float_t m_outer_range_max; float_t m_inner_shadow_range_max;
float_t m_outer_offset_x; float_t m_inner_shadow_offset_x;
float_t m_outer_offset_y; float_t m_inner_shadow_offset_y;
uint32_t m_outer_color; /// Outer Shadow
bool m_outer_shadow;
vec4 m_outer_shadow_color;
float_t m_outer_shadow_range_min;
float_t m_outer_shadow_range_max;
float_t m_outer_shadow_offset_x;
float_t m_outer_shadow_offset_y;
/// Inner Glow
bool m_inner_glow;
vec4 m_inner_glow_color;
float_t m_inner_glow_width;
float_t m_inner_glow_sharpness;
float_t m_inner_glow_sharpness_inv;
/// Outer Glow
bool m_outer_glow;
vec4 m_outer_glow_color;
float_t m_outer_glow_width;
float_t m_outer_glow_sharpness;
float_t m_outer_glow_sharpness_inv;
/// Outline
bool m_outline;
vec4 m_outline_color;
float_t m_outline_width;
float_t m_outline_offset;
float_t m_outline_sharpness;
float_t m_outline_sharpness_inv;
static bool cb_modified_inside(void* ptr, obs_properties_t* props, obs_property* prop, static bool cb_modified_shadow_inside(void* ptr, obs_properties_t* props, obs_property* prop,
obs_data_t* settings); obs_data_t* settings);
static bool cb_modified_outside(void* ptr, obs_properties_t* props, obs_property* prop, static bool cb_modified_shadow_outside(void* ptr, obs_properties_t* props, obs_property* prop,
obs_data_t* settings);
static bool cb_modified_glow_inside(void* ptr, obs_properties_t* props, obs_property* prop,
obs_data_t* settings);
static bool cb_modified_glow_outside(void* ptr, obs_properties_t* props, obs_property* prop,
obs_data_t* settings);
static bool cb_modified_outline(void* ptr, obs_properties_t* props, obs_property* prop,
obs_data_t* settings); obs_data_t* settings);
static bool cb_modified_advanced(void* ptr, obs_properties_t* props, obs_property* prop, static bool cb_modified_advanced(void* ptr, obs_properties_t* props, obs_property* prop,