From 609c7dfa2a79addcbabb47ba78b7e423591a0be9 Mon Sep 17 00:00:00 2001 From: Colin Edwards Date: Sun, 24 Feb 2019 01:03:09 -0600 Subject: [PATCH] obs-filters: Add luma key filter --- plugins/obs-filters/CMakeLists.txt | 3 +- plugins/obs-filters/data/locale/en-US.ini | 5 + .../obs-filters/data/luma_key_filter.effect | 51 ++++++ plugins/obs-filters/luma-key-filter.c | 153 ++++++++++++++++++ plugins/obs-filters/obs-filters.c | 2 + 5 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 plugins/obs-filters/data/luma_key_filter.effect create mode 100644 plugins/obs-filters/luma-key-filter.c diff --git a/plugins/obs-filters/CMakeLists.txt b/plugins/obs-filters/CMakeLists.txt index accfbe254..d6cf13c28 100644 --- a/plugins/obs-filters/CMakeLists.txt +++ b/plugins/obs-filters/CMakeLists.txt @@ -51,7 +51,8 @@ set(obs-filters_SOURCES invert-audio-polarity.c compressor-filter.c limiter-filter.c - expander-filter.c) + expander-filter.c + luma-key-filter.c) add_library(obs-filters MODULE ${obs-filters_SOURCES} diff --git a/plugins/obs-filters/data/locale/en-US.ini b/plugins/obs-filters/data/locale/en-US.ini index b806f9ddc..8821d24bb 100644 --- a/plugins/obs-filters/data/locale/en-US.ini +++ b/plugins/obs-filters/data/locale/en-US.ini @@ -92,3 +92,8 @@ Expander.None="None" Expander.Presets="Presets" Expander.Presets.Expander="Expander" Expander.Presets.Gate="Gate" +LumaKeyFilter="Luma Key" +Luma.LumaMax="Luma Max" +Luma.LumaMin="Luma Min" +Luma.LumaMaxSmooth="Luma Max Smooth" +Luma.LumaMinSmooth="Luma Min Smooth" diff --git a/plugins/obs-filters/data/luma_key_filter.effect b/plugins/obs-filters/data/luma_key_filter.effect new file mode 100644 index 000000000..77cd40dfc --- /dev/null +++ b/plugins/obs-filters/data/luma_key_filter.effect @@ -0,0 +1,51 @@ +uniform float4x4 ViewProj; +uniform texture2d image; + +uniform float lumaMax; +uniform float lumaMin; +uniform float lumaMaxSmooth; +uniform float lumaMinSmooth; + +sampler_state textureSampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +VertData VSDefault(VertData v_in) +{ + VertData vert_out; + vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + vert_out.uv = v_in.uv; + return vert_out; +} + +float4 PSALumaKeyRGBA(VertData v_in) : TARGET +{ + float4 rgba = image.Sample(textureSampler, v_in.uv); + + float4 lumaCoef = float4(0.2989, 0.5870, 0.1140, 0.0); + + float luminance = dot(rgba, lumaCoef); + + float clo = smoothstep(lumaMin, lumaMin + lumaMinSmooth, luminance); + float chi = 1. - smoothstep(lumaMax - lumaMaxSmooth, lumaMax, luminance); + + float amask = clo * chi; + + return float4(rgba.rgb, amask); +} + +technique Draw +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSALumaKeyRGBA(v_in); + } +} diff --git a/plugins/obs-filters/luma-key-filter.c b/plugins/obs-filters/luma-key-filter.c new file mode 100644 index 000000000..b738a1c19 --- /dev/null +++ b/plugins/obs-filters/luma-key-filter.c @@ -0,0 +1,153 @@ +#include + +#define SETTING_LUMA_MAX "luma_max" +#define SETTING_LUMA_MIN "luma_min" +#define SETTING_LUMA_MAX_SMOOTH "luma_max_smooth" +#define SETTING_LUMA_MIN_SMOOTH "luma_min_smooth" + +#define TEXT_LUMA_MAX obs_module_text("Luma.LumaMax") +#define TEXT_LUMA_MIN obs_module_text("Luma.LumaMin") +#define TEXT_LUMA_MAX_SMOOTH obs_module_text("Luma.LumaMaxSmooth") +#define TEXT_LUMA_MIN_SMOOTH obs_module_text("Luma.LumaMinSmooth") + +struct luma_key_filter_data { + obs_source_t *context; + + gs_effect_t *effect; + + gs_eparam_t *luma_max_param; + gs_eparam_t *luma_min_param; + gs_eparam_t *luma_max_smooth_param; + gs_eparam_t *luma_min_smooth_param; + + float luma_max; + float luma_min; + float luma_max_smooth; + float luma_min_smooth; +}; + +static const char *luma_key_name(void *unused) +{ + UNUSED_PARAMETER(unused); + return obs_module_text("LumaKeyFilter"); +} + +static void luma_key_update(void *data, obs_data_t *settings) +{ + struct luma_key_filter_data *filter = data; + + double lumaMax = obs_data_get_double(settings, SETTING_LUMA_MAX); + double lumaMin = obs_data_get_double(settings, SETTING_LUMA_MIN); + double lumaMaxSmooth = obs_data_get_double(settings, SETTING_LUMA_MAX_SMOOTH); + double lumaMinSmooth = obs_data_get_double(settings, SETTING_LUMA_MIN_SMOOTH); + + filter->luma_max = (float)lumaMax; + filter->luma_min = (float)lumaMin; + filter->luma_max_smooth = (float)lumaMaxSmooth; + filter->luma_min_smooth = (float)lumaMinSmooth; +} + +static void luma_key_destroy(void *data) +{ + struct luma_key_filter_data *filter = data; + + if (filter->effect) { + obs_enter_graphics(); + gs_effect_destroy(filter->effect); + obs_leave_graphics(); + } + + bfree(data); +} + +static void *luma_key_create(obs_data_t *settings, obs_source_t *context) +{ + struct luma_key_filter_data *filter = + bzalloc(sizeof(struct luma_key_filter_data)); + char *effect_path = obs_module_file("luma_key_filter.effect"); + + filter->context = context; + + obs_enter_graphics(); + + filter->effect = gs_effect_create_from_file(effect_path, NULL); + if (filter->effect) { + filter->luma_max_param = gs_effect_get_param_by_name( + filter->effect, "lumaMax"); + filter->luma_min_param = gs_effect_get_param_by_name( + filter->effect, "lumaMin"); + filter->luma_max_smooth_param = gs_effect_get_param_by_name( + filter->effect, "lumaMaxSmooth"); + filter->luma_min_smooth_param = gs_effect_get_param_by_name( + filter->effect, "lumaMinSmooth"); + } + + obs_leave_graphics(); + + bfree(effect_path); + + if (!filter->effect) { + luma_key_destroy(filter); + return NULL; + } + + luma_key_update(filter, settings); + return filter; +} + +static void luma_key_render(void *data, gs_effect_t *effect) +{ + struct luma_key_filter_data *filter = data; + + if (!obs_source_process_filter_begin(filter->context, GS_RGBA, + OBS_ALLOW_DIRECT_RENDERING)) + return; + + gs_effect_set_float(filter->luma_max_param, filter->luma_max); + gs_effect_set_float(filter->luma_min_param, filter->luma_min); + gs_effect_set_float(filter->luma_max_smooth_param, filter->luma_max_smooth); + gs_effect_set_float(filter->luma_min_smooth_param, filter->luma_min_smooth); + + obs_source_process_filter_end(filter->context, filter->effect, 0, 0); + + UNUSED_PARAMETER(effect); +} + +static obs_properties_t *luma_key_properties(void *data) +{ + obs_properties_t *props = obs_properties_create(); + + obs_properties_add_float_slider(props, SETTING_LUMA_MAX, + TEXT_LUMA_MAX, 0, 1, 0.01); + obs_properties_add_float_slider(props, SETTING_LUMA_MAX_SMOOTH, + TEXT_LUMA_MAX_SMOOTH, 0, 1, 0.01); + obs_properties_add_float_slider(props, SETTING_LUMA_MIN, + TEXT_LUMA_MIN, 0, 1, 0.01); + obs_properties_add_float_slider(props, SETTING_LUMA_MIN_SMOOTH, + TEXT_LUMA_MIN_SMOOTH, 0, 1, 0.01); + + UNUSED_PARAMETER(data); + return props; +} + +static void luma_key_defaults(obs_data_t *settings) +{ + obs_data_set_default_double(settings, SETTING_LUMA_MAX, 1.0); + obs_data_set_default_double(settings, SETTING_LUMA_MIN, 0.0); + obs_data_set_default_double(settings, SETTING_LUMA_MAX_SMOOTH, 0.0); + obs_data_set_default_double(settings, SETTING_LUMA_MIN_SMOOTH, 0.0); +} + + +struct obs_source_info luma_key_filter = { + .id = "luma_key_filter", + .type = OBS_SOURCE_TYPE_FILTER, + .output_flags = OBS_SOURCE_VIDEO, + .get_name = luma_key_name, + .create = luma_key_create, + .destroy = luma_key_destroy, + .video_render = luma_key_render, + .update = luma_key_update, + .get_properties = luma_key_properties, + .get_defaults = luma_key_defaults +}; diff --git a/plugins/obs-filters/obs-filters.c b/plugins/obs-filters/obs-filters.c index 09096a132..c60e6eef4 100644 --- a/plugins/obs-filters/obs-filters.c +++ b/plugins/obs-filters/obs-filters.c @@ -28,6 +28,7 @@ extern struct obs_source_info noise_gate_filter; extern struct obs_source_info compressor_filter; extern struct obs_source_info limiter_filter; extern struct obs_source_info expander_filter; +extern struct obs_source_info luma_key_filter; bool obs_module_load(void) { @@ -51,5 +52,6 @@ bool obs_module_load(void) obs_register_source(&compressor_filter); obs_register_source(&limiter_filter); obs_register_source(&expander_filter); + obs_register_source(&luma_key_filter); return true; }