From 03190c14a7b14ca86ab290a27e51699c81ea4822 Mon Sep 17 00:00:00 2001 From: FiniteSingularity <72580859+FiniteSingularity@users.noreply.github.com> Date: Thu, 20 Jun 2024 14:00:43 -0500 Subject: [PATCH] Working analog glitch effect. (#40) --- CMakeLists.txt | 2 + data/locale/en-US.ini | 19 ++ data/shaders/analog-glitch.effect | 104 +++++++ data/shaders/noise-functions.effect | 161 +++++++++++ src/filters/analog-glitch.c | 422 ++++++++++++++++++++++++++++ src/filters/analog-glitch.h | 63 +++++ src/obs-retro-effects-filter.c | 7 + src/obs-retro-effects.h | 2 + 8 files changed, 780 insertions(+) create mode 100644 data/shaders/analog-glitch.effect create mode 100644 src/filters/analog-glitch.c create mode 100644 src/filters/analog-glitch.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e5060e..0b1bdff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,8 @@ target_sources(${PROJECT_NAME} PRIVATE src/blur/blur.h src/blur/gaussian-kernel.c src/blur/gaussian-kernel.h + src/filters/analog-glitch.c + src/filters/analog-glitch.h src/filters/bloom-f.c src/filters/bloom-f.h src/filters/cathode-boot.c diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index a7c6255..c4e79c2 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -169,3 +169,22 @@ RetroEffects.DigitalGlitch.MinRGBHeight="Min Height" RetroEffects.DigitalGlitch.MaxRGBHeight="Max Height" RetroEffects.DigitalGlitch.MinRGBInterval="Min Interval" RetroEffects.DigitalGlitch.MaxRGBInterval="Max Interval" +RetroEffects.AnalogGlitch="Analog Glitch" +RetroEffects.AnalogGlitch.Primary="Primary Wave" +RetroEffects.AnalogGlitch.Primary.Speed="Speed" +RetroEffects.AnalogGlitch.Primary.Scale="Scale" +RetroEffects.AnalogGlitch.Primary.Threshold="Threshold" +RetroEffects.AnalogGlitch.Secondary="Secondary Wave" +RetroEffects.AnalogGlitch.Secondary.Speed="Speed" +RetroEffects.AnalogGlitch.Secondary.Scale="Scale" +RetroEffects.AnalogGlitch.Secondary.Threshold="Threshold" +RetroEffects.AnalogGlitch.Secondary.Influence="Influence" +RetroEffects.AnalogGlitch.MaximumDisplacement="Max Displacement" +RetroEffects.AnalogGlitch.Interference="Interference" +RetroEffects.AnalogGlitch.Interference.Magnitude="Interference Magnitude" +RetroEffects.AnalogGlitch.Interference.AffectsAlpha="Alpha Channel?" +RetroEffects.AnalogGlitch.Line.Magnitude="Line Magnitude" +RetroEffects.AnalogGlitch.CA="Color Drift" +RetroEffects.AnalogGlitch.CA.MaxDisp="Maximum Distance" +RetroEffects.AnalogGlitch.DeSat="Desaturation" +RetroEffects.AnalogGlitch.DeSat.Amount="Amount" diff --git a/data/shaders/analog-glitch.effect b/data/shaders/analog-glitch.effect new file mode 100644 index 0000000..ad3bbc8 --- /dev/null +++ b/data/shaders/analog-glitch.effect @@ -0,0 +1,104 @@ +// Based off of ShaderToy "Video Glitch" by dyvoid. +// https://www.shadertoy.com/view/XtK3W3 +// Converted to HLSL, added user changable parameters, +// And added CA effect by Finite Singularity. + +uniform float4x4 ViewProj; +uniform texture2d image; +uniform float2 uv_size; +uniform float time; + +uniform float speed_primary; //= 2.0; +uniform float speed_secondary; // = 5.0; +uniform float scale_primary; // = 800.0; +uniform float scale_secondary; // = 128.0; + +uniform float threshold_primary; // = 0.3; +uniform float threshold_secondary; // = 0.7; +uniform float secondary_influence; // = 0.15; + +uniform float max_disp; // = 250.0; +uniform float interference_mag; // = 0.3; +uniform float line_mag; // = 0.15; +uniform float interference_alpha; + +uniform float desaturation_amount; +uniform float color_drift; + +#include "noise-functions.effect" + +sampler_state textureSampler{ + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; + MinLOD = 0; + MaxLOD = 0; +}; + +struct VertData +{ + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +VertData mainTransform(VertData v_in) +{ + v_in.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + return v_in; +} + +// Sample with desaturation +float4 sampleDesaturatedTexture(float2 xy, float desat) +{ + // Desaturation Distortion + float4 col = image.Sample(textureSampler, xy / uv_size); + float lum = dot(col.rgb, float3(0.299, 0.587, 0.114)); + col.rgb = lerp(col.rgb, float3(lum, lum, lum), float3(desat, desat, desat)); + return col; +} + +float4 mainImage(VertData v_in) : TARGET +{ + float2 coord = v_in.uv * uv_size; + + float n_primary = open_simplex_1d(float2(time * speed_primary, coord.y), 0.0, float2(1.0, scale_primary)); + float n_secondary = open_simplex_1d(float2(time * speed_secondary, coord.y), 0.0, float2(1.0, scale_secondary)); + + n_primary = max(0.0, (n_primary - threshold_primary)) / (1.0 - threshold_primary); + n_primary += n_secondary * secondary_influence; + n_primary /= (1.0 + secondary_influence); + + coord.x = coord.x - max_disp * n_primary * n_primary; + + // CA Distortion and Desaturation + float desat = n_primary * desaturation_amount; + float4 col_g = sampleDesaturatedTexture(coord, desat); + float4 col_r = sampleDesaturatedTexture(coord - float2(n_primary * color_drift, 0.0), desat); + float4 col_b = sampleDesaturatedTexture(coord + float2(n_primary * color_drift, 0.0), desat); + + float4 col = float4(col_r.r, col_g.g, col_b.b, (col_r.a + col_g.a + col_b.a)*0.33334); + + // Interference noise (lightening/darkening) + float n_interference = hash11(coord.y * time*5.0); + float inter = n_primary * interference_mag; + col.rgb = lerp(col.rgb, float3(n_interference, n_interference, n_interference), float3(inter, inter, inter)); + + // Static lines (correlated to primary displacement) + if (floor(fmod(coord.y * 0.25, 2.0)) < 0.001) + { + float intf = 1.0 - (line_mag * n_primary); + col.rgb *= intf; + col.a = col.a - (interference_alpha * (1.0 - intf)); + } + + return col; +} + +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } +} diff --git a/data/shaders/noise-functions.effect b/data/shaders/noise-functions.effect index ef5e2ea..ddc331d 100644 --- a/data/shaders/noise-functions.effect +++ b/data/shaders/noise-functions.effect @@ -183,3 +183,164 @@ float2 whiteNoise2D(float2 pos, uint seed) ); #endif } + +////////////////// K.jpg's Smooth Re-oriented 8-Point BCC Noise ////////////////// +//////////////////// Output: float4(dF/dx, dF/dy, dF/dz, value) //////////////////// + +// Borrowed from Stefan Gustavson's noise code + +float4 permute(float4 t) +{ + return t * (t * 34.0 + 133.0); +} + +#ifndef OPENGL +float mod(float x, float y) +{ + return x - y * floor(x / y); +} + +float2 mod(float2 x, float2 y) +{ + return x - y * floor(x / y); +} + +float3 mod(float3 x, float3 y) +{ + return x - y * floor(x / y); +} + +float4 mod4(float4 x, float4 y) +{ + return x - y * floor(x / y); +} +#endif + +// Gradient set is a normalized expanded rhombic dodecahedron +float3 grad(float hash) +{ + // Random vertex of a cube, +/- 1 each + float3 cube = mod(floor(hash / float3(1.0, 2.0, 4.0)), 2.0) * 2.0 - 1.0; + + // Random edge of the three edges connected to that vertex + // Also a cuboctahedral vertex + // And corresponds to the face of its dual, the rhombic dodecahedron + float3 cuboct = cube; + + int index = int(hash / 16.0); + if (index == 0) + cuboct.x = 0.0; + else if (index == 1) + cuboct.y = 0.0; + else + cuboct.z = 0.0; + + // In a funky way, pick one of the four points on the rhombic face + float type = mod(floor(hash / 8.0), 2.0); + float3 rhomb = (1.0 - type) * cube + type * (cuboct + cross(cube, cuboct)); + + // Expand it so that the new edges are the same length + // as the existing ones + float3 grad = cuboct * 1.22474487139 + rhomb; + + // To make all gradients the same length, we only need to shorten the + // second type of vector. We also put in the whole noise scale constant. + // The compiler should reduce it into the existing floats. I think. + grad *= (1.0 - 0.042942436724648037 * type) * 3.5946317686139184; + + return grad; +} + +// BCC lattice split up into 2 cube lattices +float4 openSimplex2SDerivativesPart(float3 X) +{ + float3 b = floor(X); + float4 i4 = float4(X - b, 2.5); + + // Pick between each pair of oppposite corners in the cube. + float3 v1 = b + floor(dot(i4, float4(.25, .25, .25, .25))); + float3 v2 = b + float3(1, 0, 0) + float3(-1, 1, 1) * floor(dot(i4, float4(-.25, .25, .25, .35))); + float3 v3 = b + float3(0, 1, 0) + float3(1, -1, 1) * floor(dot(i4, float4(.25, -.25, .25, .35))); + float3 v4 = b + float3(0, 0, 1) + float3(1, 1, -1) * floor(dot(i4, float4(.25, .25, -.25, .35))); + + // Gradient hashes for the four vertices in this half-lattice. + float4 hashes = float4( + hash33(v1).x, hash33(v2).x, hash33(v3).x, hash33(v4).x + ) * 47.0; + + + // Gradient extrapolations & kernel function + float3 d1 = X - v1; + float3 d2 = X - v2; + float3 d3 = X - v3; + float3 d4 = X - v4; + float4 a = max(float4(0.75, 0.75, 0.75, 0.75) - float4(dot(d1, d1), dot(d2, d2), dot(d3, d3), dot(d4, d4)), float4(0.0, 0.0, 0.0, 0.0)); + float4 aa = a * a; + float4 aaaa = aa * aa; + float3 g1 = grad(hashes.x); + float3 g2 = grad(hashes.y); + float3 g3 = grad(hashes.z); + float3 g4 = grad(hashes.w); + float4 extrapolations = float4(dot(d1, g1), dot(d2, g2), dot(d3, g3), dot(d4, g4)); + +#ifndef OPENGL + float4x3 derivativeMatrix = { d1, d2, d3, d4 }; + float4x3 gradientMatrix = { g1, g2, g3, g4 }; + + // Derivatives of the noise + float3 derivative = -8.0 * mul(aa * a * extrapolations, derivativeMatrix) + + mul(aaaa, gradientMatrix); + +#else + vec3 derivative = -8.0 * mat4x3(d1, d2, d3, d4) * (aa * a * extrapolations) + mat4x3(g1, g2, g3, g4) * aaaa; +#endif + + // Return it all as a float4 + return float4(derivative, dot(aaaa, extrapolations)); +} + +// Use this if you don't want Z to look different from X and Y +float4 openSimplex2SDerivatives_Conventional(float3 X) +{ + X = dot(X, float3(2.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0)) - X; + + float4 result = openSimplex2SDerivativesPart(X) + openSimplex2SDerivativesPart(X + 144.5); + + return float4(dot(result.xyz, float3(2.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0)) - result.xyz, result.w); +} + +// Use this if you want to show X and Y in a plane, then use Z for time, vertical, etc. +float4 openSimplex2SDerivatives_ImproveXY(float3 X) +{ +#ifndef OPENGL + // Not a skew transform. + float3x3 orthonormalMap = + { + 0.788675134594813, -0.211324865405187, -0.577350269189626, + -0.211324865405187, 0.788675134594813, -0.577350269189626, + 0.577350269189626, 0.577350269189626, 0.577350269189626 + }; + + X = mul(X, orthonormalMap); + float4 result = openSimplex2SDerivativesPart(X) + openSimplex2SDerivativesPart(X + 144.5); + return float4(mul(orthonormalMap, result.xyz), result.w); +#else + mat3 orthonormalMap = mat3( + 0.788675134594813, -0.211324865405187, -0.577350269189626, + -0.211324865405187, 0.788675134594813, -0.577350269189626, + 0.577350269189626, 0.577350269189626, 0.577350269189626); + + X = orthonormalMap * X; + float4 result = openSimplex2SDerivativesPart(X) + openSimplex2SDerivativesPart(X + 144.5); + + return vec4(result.xyz * orthonormalMap, result.w); +#endif +} + +float open_simplex_1d(float2 coord, float t, float2 scale) +{ + coord *= 1.0 / (scale); + float4 noise = openSimplex2SDerivatives_Conventional(float3(coord, t)); + return noise.w; + //return noise.w * 0.5 + 0.5; +} diff --git a/src/filters/analog-glitch.c b/src/filters/analog-glitch.c new file mode 100644 index 0000000..273701e --- /dev/null +++ b/src/filters/analog-glitch.c @@ -0,0 +1,422 @@ +#include "analog-glitch.h" +#include "../obs-utils.h" + +void analog_glitch_create(retro_effects_filter_data_t *filter) +{ + analog_glitch_filter_data_t *data = + bzalloc(sizeof(analog_glitch_filter_data_t)); + filter->active_filter_data = data; + data->reload_effect = false; + analog_glitch_set_functions(filter); + obs_data_t *settings = obs_source_get_settings(filter->base->context); + analog_glitch_filter_defaults(settings); + obs_data_release(settings); + analog_glitch_load_effect(data); +} + +void analog_glitch_destroy(retro_effects_filter_data_t *filter) +{ + analog_glitch_filter_data_t *data = filter->active_filter_data; + obs_enter_graphics(); + if (data->effect_analog_glitch) { + gs_effect_destroy(data->effect_analog_glitch); + } + + obs_leave_graphics(); + + obs_data_t *settings = obs_source_get_settings(filter->base->context); + // obs_data_unset_user_value(settings, "analog_glitch_size"); + obs_data_unset_user_value(settings, "analog_glitch_primary_speed"); + obs_data_unset_user_value(settings, "analog_glitch_primary_scale"); + obs_data_unset_user_value(settings, "analog_glitch_secondary_speed"); + obs_data_unset_user_value(settings, "analog_glitch_secondary_scale"); + obs_data_unset_user_value(settings, "analog_glitch_interference_speed"); + obs_data_unset_user_value(settings, "analog_glitch_interference_scale"); + obs_data_unset_user_value(settings, "analog_glitch_primary_threshold"); + obs_data_unset_user_value(settings, "analog_glitch_secondary_threshold"); + obs_data_unset_user_value(settings, "analog_glitch_secondary_influence"); + obs_data_unset_user_value(settings, "analog_glitch_max_disp"); + obs_data_unset_user_value(settings, "analog_glitch_interference_magnitude"); + obs_data_unset_user_value(settings, "analog_glitch_line_magnitude"); + obs_data_unset_user_value(settings, "analog_glitch_interfence_alpha"); + obs_data_release(settings); + + bfree(filter->active_filter_data); + filter->active_filter_data = NULL; +} + +void analog_glitch_filter_update(retro_effects_filter_data_t *data, + obs_data_t *settings) +{ + analog_glitch_filter_data_t *filter = data->active_filter_data; + filter->speed_primary = (float)obs_data_get_double(settings, "analog_glitch_primary_speed"); + filter->scale_primary = (float)obs_data_get_double(settings, "analog_glitch_primary_scale"); + filter->speed_secondary = (float)obs_data_get_double(settings, "analog_glitch_secondary_speed"); + filter->scale_secondary = (float)obs_data_get_double(settings, "analog_glitch_secondary_scale"); + + filter->threshold_primary = (float)obs_data_get_double(settings, "analog_glitch_primary_threshold"); + filter->threshold_secondary = (float)obs_data_get_double(settings, "analog_glitch_secondary_threshold"); + filter->secondary_influence = (float)obs_data_get_double(settings, "analog_glitch_secondary_influence"); + filter->max_disp = (float)obs_data_get_double(settings, "analog_glitch_max_disp"); + filter->interference_mag = (float)obs_data_get_double(settings, "analog_glitch_interference_magnitude"); + filter->line_mag = (float)obs_data_get_double(settings, "analog_glitch_line_magnitude"); + filter->interference_alpha = obs_data_get_bool(settings, "analog_glitch_interfence_alpha"); + filter->desaturation_amount = (float)obs_data_get_double(settings, "analog_glitch_desat_amount")/100.0f; + filter->color_drift = (float)obs_data_get_double(settings, "analog_glitch_ca_max_disp"); + + if (filter->reload_effect) { + filter->reload_effect = false; + analog_glitch_load_effect(filter); + } +} + +void analog_glitch_filter_defaults(obs_data_t *settings) +{ + obs_data_set_default_double(settings, "analog_glitch_primary_speed", 2.0); + obs_data_set_default_double(settings, "analog_glitch_primary_scale", 800.0); + obs_data_set_default_double(settings, "analog_glitch_secondary_speed", 5.0); + obs_data_set_default_double(settings, "analog_glitch_secondary_scale", 128.0); + obs_data_set_default_double(settings, "analog_glitch_interference_speed", 5.0); + obs_data_set_default_double(settings, "analog_glitch_interference_scale", 64.0); + obs_data_set_default_double(settings, "analog_glitch_primary_threshold", 0.3); + obs_data_set_default_double(settings, "analog_glitch_secondary_threshold", 0.7); + obs_data_set_default_double(settings, "analog_glitch_secondary_influence", 0.15); + obs_data_set_default_double(settings, "analog_glitch_max_disp", 250.0); + obs_data_set_default_double(settings, "analog_glitch_interference_magnitude", 0.3); + obs_data_set_default_double(settings, "analog_glitch_line_magnitude", 0.15); + obs_data_set_default_bool(settings, "analog_glitch_interfence_alpha", false); +} + +void analog_glitch_filter_properties(retro_effects_filter_data_t *data, + obs_properties_t *props) +{ + UNUSED_PARAMETER(data); + obs_property_t *p = NULL; + + p = obs_properties_add_float_slider( + props, "analog_glitch_max_disp", + obs_module_text( + "RetroEffects.AnalogGlitch.MaximumDisplacement"), + 0.0, 6000.0, 1.0); + obs_property_float_set_suffix(p, "px"); + + obs_properties_t *primary_wave_group = obs_properties_create(); + + p = obs_properties_add_float_slider( + primary_wave_group, "analog_glitch_primary_speed", + obs_module_text("RetroEffects.AnalogGlitch.Primary.Speed"), 0.0, 100.0, + 0.01); + obs_property_float_set_suffix(p, " s"); + p = obs_properties_add_float_slider( + primary_wave_group, "analog_glitch_primary_scale", + obs_module_text("RetroEffects.AnalogGlitch.Primary.Scale"), 0.0, 1000.0, + 0.1); + obs_property_float_set_suffix(p, "px"); + + p = obs_properties_add_float_slider( + primary_wave_group, "analog_glitch_primary_threshold", + obs_module_text("RetroEffects.AnalogGlitch.Primary.Threshold"), 0.0, 1.0, + 0.01); + + obs_properties_add_group( + props, "analog_glitch_primary", + obs_module_text("RetroEffects.AnalogGlitch.Primary"), + OBS_GROUP_NORMAL, primary_wave_group); + + obs_properties_t *secondary_wave_group = obs_properties_create(); + + p = obs_properties_add_float_slider( + secondary_wave_group, "analog_glitch_secondary_speed", + obs_module_text("RetroEffects.AnalogGlitch.Secondary.Speed"), + 0.0, 100.0, 0.01); + + p = obs_properties_add_float_slider( + secondary_wave_group, "analog_glitch_secondary_scale", + obs_module_text("RetroEffects.AnalogGlitch.Secondary.Scale"), + 0.0, 1000.0, 0.1); + + p = obs_properties_add_float_slider( + secondary_wave_group, "analog_glitch_secondary_threshold", + obs_module_text( + "RetroEffects.AnalogGlitch.Secondary.Threshold"), + 0.0, 1.0, 0.01); + + p = obs_properties_add_float_slider( + secondary_wave_group, "analog_glitch_secondary_influence", + obs_module_text("RetroEffects.AnalogGlitch.Secondary.Influence"), 0.0, + 1.0, 0.01); + + obs_properties_add_group( + props, "analog_glitch_secondary", + obs_module_text("RetroEffects.AnalogGlitch.Secondary"), + OBS_GROUP_NORMAL, secondary_wave_group); + + obs_properties_t *interference_group = obs_properties_create(); + + p = obs_properties_add_float_slider( + interference_group, "analog_glitch_interference_magnitude", + obs_module_text("RetroEffects.AnalogGlitch.Interference.Magnitude"), 0.0, + 1.0, 0.01); + + p = obs_properties_add_float_slider( + interference_group, "analog_glitch_line_magnitude", + obs_module_text("RetroEffects.AnalogGlitch.Line.Magnitude"), 0.0, + 1.0, 0.01); + + p = obs_properties_add_bool(interference_group, "analog_glitch_interfence_alpha", + obs_module_text( + "RetroEffects.AnalogGlitch.Interference.AffectsAlpha")); + + obs_properties_add_group( + props, "analog_glitch_interference", + obs_module_text("RetroEffects.AnalogGlitch.Interference"), + OBS_GROUP_NORMAL, interference_group); + + obs_properties_t *ca_group = obs_properties_create(); + + p = obs_properties_add_float_slider( + ca_group, "analog_glitch_ca_max_disp", + obs_module_text( + "RetroEffects.AnalogGlitch.CA.MaxDisp"), + 0.0, 250.0, 1.0); + + obs_properties_add_group( + props, "analog_glitch_ca", + obs_module_text("RetroEffects.AnalogGlitch.CA"), + OBS_GROUP_NORMAL, ca_group); + + obs_properties_t *desat_group = obs_properties_create(); + + p = obs_properties_add_float_slider( + desat_group, "analog_glitch_desat_amount", + obs_module_text("RetroEffects.AnalogGlitch.DeSat.Amount"), 0.0, + 100.0, 0.1); + + obs_property_float_set_suffix(p, "%"); + + obs_properties_add_group( + props, "analog_glitch_desat", + obs_module_text("RetroEffects.AnalogGlitch.DeSat"), + OBS_GROUP_NORMAL, desat_group); +} + + +void analog_glitch_filter_video_render(retro_effects_filter_data_t *data) +{ + base_filter_data_t *base = data->base; + analog_glitch_filter_data_t *filter = data->active_filter_data; + + get_input_source(base); + if (!base->input_texture_generated || filter->loading_effect) { + base->rendering = false; + obs_source_skip_video_filter(base->context); + return; + } + + gs_texture_t *image = gs_texrender_get_texture(base->input_texrender); + gs_effect_t *effect = filter->effect_analog_glitch; + + if (!effect || !image) { + return; + } + + base->output_texrender = + create_or_reset_texrender(base->output_texrender); + + if (filter->param_uv_size) { + struct vec2 uv_size; + uv_size.x = (float)base->width; + uv_size.y = (float)base->height; + gs_effect_set_vec2(filter->param_uv_size, &uv_size); + } + if (filter->param_image) { + gs_effect_set_texture(filter->param_image, image); + } + + if (filter->param_time) { + gs_effect_set_float(filter->param_time, filter->time); + } + + if (filter->param_scale_primary) { + gs_effect_set_float(filter->param_scale_primary, + filter->scale_primary); + } + if (filter->param_speed_primary) { + gs_effect_set_float(filter->param_speed_primary, + filter->speed_primary); + } + + if (filter->param_scale_secondary) { + gs_effect_set_float(filter->param_scale_secondary, + filter->scale_secondary); + } + if (filter->param_speed_secondary) { + gs_effect_set_float(filter->param_speed_secondary, + filter->speed_secondary); + } + + if (filter->param_threshold_primary) { + gs_effect_set_float(filter->param_threshold_primary, + filter->threshold_primary); + } + if (filter->param_threshold_secondary) { + gs_effect_set_float(filter->param_threshold_secondary, + filter->threshold_secondary); + } + + if (filter->param_secondary_influence) { + gs_effect_set_float(filter->param_secondary_influence, + filter->secondary_influence); + } + + if (filter->param_max_disp) { + gs_effect_set_float(filter->param_max_disp, + filter->max_disp); + } + if (filter->param_interference_mag) { + gs_effect_set_float(filter->param_interference_mag, + filter->interference_mag); + } + if (filter->param_line_mag) { + gs_effect_set_float(filter->param_line_mag, + filter->line_mag); + } + + if (filter->param_desaturation_amount) { + gs_effect_set_float(filter->param_desaturation_amount, + filter->desaturation_amount); + } + + if (filter->param_color_drift) { + gs_effect_set_float(filter->param_color_drift, + filter->color_drift); + } + + if (filter->param_interference_alpha) { + float int_alpha = filter->interference_alpha ? 1.0f : 0.0f; + gs_effect_set_float(filter->param_interference_alpha, + int_alpha); + } + + set_render_parameters(); + set_blending_parameters(); + + //const char *technique = filter->monochromatic ? "DrawMonoOrdered" : "DrawColorOrdered"; + struct dstr technique; + dstr_init_copy(&technique, "Draw"); + + if (gs_texrender_begin(base->output_texrender, base->width, + base->height)) { + gs_ortho(0.0f, (float)base->width, 0.0f, (float)base->height, + -100.0f, 100.0f); + while (gs_effect_loop(effect, technique.array)) + gs_draw_sprite(image, 0, base->width, base->height); + gs_texrender_end(base->output_texrender); + } + dstr_free(&technique); + gs_blend_state_pop(); +} + +static void analog_glitch_set_functions(retro_effects_filter_data_t *filter) +{ + filter->filter_properties = analog_glitch_filter_properties; + filter->filter_video_render = analog_glitch_filter_video_render; + filter->filter_destroy = analog_glitch_destroy; + filter->filter_defaults = analog_glitch_filter_defaults; + filter->filter_update = analog_glitch_filter_update; + filter->filter_video_tick = analog_glitch_filter_video_tick; +} + +void analog_glitch_filter_video_tick(retro_effects_filter_data_t *data, + float seconds) +{ + analog_glitch_filter_data_t *filter = data->active_filter_data; + filter->time += seconds; +} + +static void analog_glitch_load_effect(analog_glitch_filter_data_t *filter) +{ + filter->loading_effect = true; + if (filter->effect_analog_glitch != NULL) { + obs_enter_graphics(); + gs_effect_destroy(filter->effect_analog_glitch); + filter->effect_analog_glitch = NULL; + obs_leave_graphics(); + } + + char *shader_text = NULL; + struct dstr filename = {0}; + dstr_cat(&filename, obs_get_module_data_path(obs_current_module())); + dstr_cat(&filename, "/shaders/analog-glitch.effect"); + shader_text = load_shader_from_file(filename.array); + char *errors = NULL; + dstr_free(&filename); + + struct dstr shader_dstr = {0}; + dstr_init_copy(&shader_dstr, shader_text); + + //dstr_cat(&shader_dstr, shader_text); + + obs_enter_graphics(); + int device_type = gs_get_device_type(); + if (device_type == GS_DEVICE_OPENGL) { + dstr_insert(&shader_dstr, 0, "#define OPENGL 1\n"); + } + filter->effect_analog_glitch = + gs_effect_create(shader_dstr.array, NULL, &errors); + obs_leave_graphics(); + + dstr_free(&shader_dstr); + bfree(shader_text); + if (filter->effect_analog_glitch == NULL) { + blog(LOG_WARNING, + "[obs-retro-effects] Unable to load analog-gitch.effect file. Errors:\n%s", + (errors == NULL || strlen(errors) == 0 ? "(None)" + : errors)); + bfree(errors); + } else { + size_t effect_count = + gs_effect_get_num_params(filter->effect_analog_glitch); + for (size_t effect_index = 0; effect_index < effect_count; + effect_index++) { + gs_eparam_t *param = gs_effect_get_param_by_idx( + filter->effect_analog_glitch, effect_index); + struct gs_effect_param_info info; + gs_effect_get_param_info(param, &info); + if (strcmp(info.name, "image") == 0) { + filter->param_image = param; + } else if (strcmp(info.name, "uv_size") == 0) { + filter->param_uv_size = param; + } else if (strcmp(info.name, "time") == 0) { + filter->param_time = param; + } else if (strcmp(info.name, "speed_primary") == 0) { + filter->param_speed_primary = param; + } else if (strcmp(info.name, "speed_secondary") == 0) { + filter->param_speed_secondary = param; + } else if (strcmp(info.name, "scale_primary") == 0) { + filter->param_scale_primary = param; + } else if (strcmp(info.name, "scale_secondary") == 0) { + filter->param_scale_secondary = param; + } else if (strcmp(info.name, "threshold_primary") == 0) { + filter->param_threshold_primary = param; + } else if (strcmp(info.name, "threshold_secondary") == 0) { + filter->param_threshold_secondary = param; + } else if (strcmp(info.name, "secondary_influence") == 0) { + filter->param_secondary_influence = param; + } else if (strcmp(info.name, "max_disp") == 0) { + filter->param_max_disp = param; + } else if (strcmp(info.name, "interference_mag") == 0) { + filter->param_interference_mag = param; + } else if (strcmp(info.name, "line_mag") == 0) { + filter->param_line_mag = param; + } else if (strcmp(info.name, "desaturation_amount") == 0) { + filter->param_desaturation_amount = param; + } else if (strcmp(info.name, "color_drift") == 0) { + filter->param_color_drift = param; + } else if (strcmp(info.name, "interference_alpha") == 0) { + filter->param_interference_alpha = param; + } + } + } + filter->loading_effect = false; +} diff --git a/src/filters/analog-glitch.h b/src/filters/analog-glitch.h new file mode 100644 index 0000000..f5ddb8e --- /dev/null +++ b/src/filters/analog-glitch.h @@ -0,0 +1,63 @@ +#include +#include "../obs-retro-effects.h" + +struct analog_glitch_filter_data; +typedef struct analog_glitch_filter_data analog_glitch_filter_data_t; + +struct analog_glitch_filter_data { + gs_effect_t *effect_analog_glitch; + + gs_eparam_t *param_image; + gs_eparam_t *param_uv_size; + gs_eparam_t *param_time; + gs_eparam_t *param_speed_primary; + gs_eparam_t *param_speed_secondary; + gs_eparam_t *param_speed_interference; + gs_eparam_t *param_scale_primary; + gs_eparam_t *param_scale_secondary; + gs_eparam_t *param_scale_interference; + gs_eparam_t *param_threshold_primary; + gs_eparam_t *param_threshold_secondary; + gs_eparam_t *param_secondary_influence; + gs_eparam_t *param_max_disp; + gs_eparam_t *param_interference_mag; + gs_eparam_t *param_line_mag; + gs_eparam_t *param_desaturation_amount; + gs_eparam_t *param_color_drift; + gs_eparam_t *param_interference_alpha; + + float time; + float speed_primary; + float speed_secondary; + float speed_interference; + float scale_primary; + float scale_secondary; + float scale_interference; + + float threshold_primary; + float threshold_secondary; + float secondary_influence; + float max_disp; + float interference_mag; + float line_mag; + bool interference_alpha; + + float desaturation_amount; + float color_drift; + + bool loading_effect; + bool reload_effect; +}; + +extern void analog_glitch_create(retro_effects_filter_data_t *filter); +extern void analog_glitch_destroy(retro_effects_filter_data_t *filter); +extern void analog_glitch_filter_video_render(retro_effects_filter_data_t *data); +extern void analog_glitch_filter_video_tick(retro_effects_filter_data_t *data, + float seconds); +extern void analog_glitch_filter_properties(retro_effects_filter_data_t *data, + obs_properties_t *props); +extern void analog_glitch_filter_defaults(obs_data_t *settings); +extern void analog_glitch_filter_update(retro_effects_filter_data_t *data, + obs_data_t *settings); +static void analog_glitch_set_functions(retro_effects_filter_data_t *filter); +static void analog_glitch_load_effect(analog_glitch_filter_data_t *filter); diff --git a/src/obs-retro-effects-filter.c b/src/obs-retro-effects-filter.c index 86d6149..89c0ba8 100644 --- a/src/obs-retro-effects-filter.c +++ b/src/obs-retro-effects-filter.c @@ -1,5 +1,6 @@ #include "obs-retro-effects-filter.h" #include "obs-retro-effects.h" +#include "filters/analog-glitch.h" #include "filters/frame-skip.h" #include "filters/interlace.h" #include "filters/chromatic-aberration.h" @@ -199,6 +200,9 @@ static obs_properties_t *retro_effects_filter_properties(void *data) obs_property_list_add_int(filter_list, obs_module_text(RETRO_FILTER_DIGITAL_GLITCH_LABEL), RETRO_FILTER_DIGITAL_GLITCH); + obs_property_list_add_int(filter_list, + obs_module_text(RETRO_FILTER_ANALOG_GLITCH_LABEL), + RETRO_FILTER_ANALOG_GLITCH); obs_property_set_modified_callback2(filter_list, filter_type_modified, data); @@ -275,6 +279,9 @@ static void load_filter(retro_effects_filter_data_t *filter, int old_type) case RETRO_FILTER_DIGITAL_GLITCH: digital_glitch_create(filter); break; + case RETRO_FILTER_ANALOG_GLITCH: + analog_glitch_create(filter); + break; } } diff --git a/src/obs-retro-effects.h b/src/obs-retro-effects.h index b6d77eb..5f08c4e 100644 --- a/src/obs-retro-effects.h +++ b/src/obs-retro-effects.h @@ -35,6 +35,8 @@ #define RETRO_FILTER_SCANLINES_LABEL "RetroEffects.Scanlines" #define RETRO_FILTER_DIGITAL_GLITCH 14 #define RETRO_FILTER_DIGITAL_GLITCH_LABEL "RetroEffects.DigitalGlitch" +#define RETRO_FILTER_ANALOG_GLITCH 15 +#define RETRO_FILTER_ANALOG_GLITCH_LABEL "RetroEffects.AnalogGlitch" struct retro_effects_filter_data; typedef struct retro_effects_filter_data retro_effects_filter_data_t;