--Corner Pin effect filter by khaver obs = obslua bit = require("bit") SETTING_TLX = 'pos_tlx' SETTING_TLY = 'pos_tly' SETTING_TRX = 'pos_trx' SETTING_TRY = 'pos_try' SETTING_BLX = 'pos_blx' SETTING_BLY = 'pos_bly' SETTING_BRX = 'pos_brx' SETTING_BRY = 'pos_bry' TEXT_TLX = 'Top Left X' TEXT_TLY = 'Top Left Y' TEXT_TRX = 'Top Right X' TEXT_TRY = 'Top Right Y' TEXT_BLX = 'Bottom Left X' TEXT_BLY = 'Bottom Left Y' TEXT_BRX = 'Bottom Right X' TEXT_BRY = 'Bottom Right Y' source_def = {} source_def.id = 'filter-cornerpin' source_def.type = obs.OBS_SOURCE_TYPE_FILTER source_def.output_flags = bit.bor(obs.OBS_SOURCE_VIDEO) function set_render_size(filter) target = obs.obs_filter_get_target(filter.context) local width, height if target == nil then width = 0 height = 0 else width = obs.obs_source_get_base_width(target) height = obs.obs_source_get_base_height(target) end filter.width = width filter.height = height end source_def.get_name = function() return "Corner Pin" end source_def.create = function(settings, source) --local effect_path = script_path() .. 'filter-cornerpin/filter-cornerpin.effect' filter = {} filter.params = {} filter.context = source set_render_size(filter) obs.obs_enter_graphics() --filter.effect = obs.gs_effect_create_from_file(effect_path, nil) filter.effect = obs.gs_effect_create(shader, nil, nil) if filter.effect ~= nil then filter.params.pos_tlx = obs.gs_effect_get_param_by_name(filter.effect, 'pos_tlx') filter.params.pos_tly = obs.gs_effect_get_param_by_name(filter.effect, 'pos_tly') filter.params.pos_trx = obs.gs_effect_get_param_by_name(filter.effect, 'pos_trx') filter.params.pos_try = obs.gs_effect_get_param_by_name(filter.effect, 'pos_try') filter.params.pos_blx = obs.gs_effect_get_param_by_name(filter.effect, 'pos_blx') filter.params.pos_bly = obs.gs_effect_get_param_by_name(filter.effect, 'pos_bly') filter.params.pos_brx = obs.gs_effect_get_param_by_name(filter.effect, 'pos_brx') filter.params.pos_bry = obs.gs_effect_get_param_by_name(filter.effect, 'pos_bry') end obs.obs_leave_graphics() if filter.effect == nil then source_def.destroy(filter) return nil end source_def.update(filter, settings) return filter end source_def.destroy = function(filter) if filter.effect ~= nil then obs.obs_enter_graphics() obs.gs_effect_destroy(filter.effect) obs.obs_leave_graphics() end end source_def.get_width = function(filter) return filter.width end source_def.get_height = function(filter) return filter.height end source_def.update = function(filter, settings) filter.pos_tlx = obs.obs_data_get_double(settings, SETTING_TLX) filter.pos_tly = obs.obs_data_get_double(settings, SETTING_TLY) filter.pos_trx = obs.obs_data_get_double(settings, SETTING_TRX) filter.pos_try = obs.obs_data_get_double(settings, SETTING_TRY) filter.pos_blx = obs.obs_data_get_double(settings, SETTING_BLX) filter.pos_bly = obs.obs_data_get_double(settings, SETTING_BLY) filter.pos_brx = obs.obs_data_get_double(settings, SETTING_BRX) filter.pos_bry = obs.obs_data_get_double(settings, SETTING_BRY) set_render_size(filter) end source_def.video_render = function(filter, effect) obs.obs_source_process_filter_begin(filter.context, obs.GS_RGBA, obs.OBS_NO_DIRECT_RENDERING) obs.gs_effect_set_float(filter.params.pos_tlx, filter.pos_tlx) obs.gs_effect_set_float(filter.params.pos_tly, filter.pos_tly) obs.gs_effect_set_float(filter.params.pos_trx, filter.pos_trx) obs.gs_effect_set_float(filter.params.pos_try, filter.pos_try) obs.gs_effect_set_float(filter.params.pos_blx, filter.pos_blx) obs.gs_effect_set_float(filter.params.pos_bly, filter.pos_bly) obs.gs_effect_set_float(filter.params.pos_brx, filter.pos_brx) obs.gs_effect_set_float(filter.params.pos_bry, filter.pos_bry) obs.obs_source_process_filter_end(filter.context, filter.effect, filter.width, filter.height) end source_def.get_properties = function(settings) props = obs.obs_properties_create() obs.obs_properties_add_float_slider(props, SETTING_TLX, TEXT_TLX, 0.0, 1.0, 0.0001) obs.obs_properties_add_float_slider(props, SETTING_TLY, TEXT_TLY, 0.0, 1.0, 0.0001) obs.obs_properties_add_float_slider(props, SETTING_TRX, TEXT_TRX, 0.0, 1.0, 0.0001) obs.obs_properties_add_float_slider(props, SETTING_TRY, TEXT_TRY, 0.0, 1.0, 0.0001) obs.obs_properties_add_float_slider(props, SETTING_BLX, TEXT_BLX, 0.0, 1.0, 0.0001) obs.obs_properties_add_float_slider(props, SETTING_BLY, TEXT_BLY, 0.0, 1.0, 0.0001) obs.obs_properties_add_float_slider(props, SETTING_BRX, TEXT_BRX, 0.0, 1.0, 0.0001) obs.obs_properties_add_float_slider(props, SETTING_BRY, TEXT_BRY, 0.0, 1.0, 0.0001) return props end source_def.get_defaults = function(settings) obs.obs_data_set_default_double(settings, SETTING_TLX, 0.0) obs.obs_data_set_default_double(settings, SETTING_TLY, 0.0) obs.obs_data_set_default_double(settings, SETTING_TRX, 0.0) obs.obs_data_set_default_double(settings, SETTING_TRY, 0.0) obs.obs_data_set_default_double(settings, SETTING_BLX, 0.0) obs.obs_data_set_default_double(settings, SETTING_BLY, 0.0) obs.obs_data_set_default_double(settings, SETTING_BRX, 0.0) obs.obs_data_set_default_double(settings, SETTING_BRY, 0.0) end source_def.video_tick = function(filter, seconds) set_render_size(filter) end obs.obs_register_source(source_def) shader = [[ //Corner Pin effect filter adapted by khaver for cornerpin.lua //Based on a shader at https://github.com/Oncorporation/obs-shaderfilter/blob/master/data/examples/corner_pin.shader //By Charles Fettinger uniform float4x4 ViewProj; uniform texture2d image; uniform float pos_tlx; uniform float pos_tly; uniform float pos_trx; uniform float pos_try; uniform float pos_blx; uniform float pos_bly; uniform float pos_brx; uniform float pos_bry; sampler_state textureSampler { 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; } float cross2d(float2 a, float2 b) { return (a.x * b.y) - (a.y * b.x); } float2 invBilinear(float2 p) { float2 a = float2(pos_tlx / 1.0, pos_tly / 1.0); float2 b = float2(1.0 - (pos_trx / 1.0), pos_try / 1.0); float2 c = float2(1.0 - (pos_brx / 1.0), 1.0 - (pos_bry / 1.0)); float2 d = float2(pos_blx / 1.0, 1.0 - (pos_bly / 1.0)); float2 e = b-a; float2 f = d-a; float2 g = a-b+c-d; float2 h = p-a; float k2 = cross2d( g, f ); float k1 = cross2d( e, f ) + cross2d( h, g ); float k0 = cross2d( h, e ); float k2u = cross2d( e, g ); float k1u = cross2d( e, f ) + cross2d( g, h ); float k0u = cross2d( h, f); float v1, u1, v2, u2; if (abs(k2) < 0.0001) { v1 = -k0 / k1; u1 = (h.x - f.x*v1)/(e.x + g.x*v1); } else if (abs(k2u) < 0.0001) { u1 = k0u / k1u; v1 = (h.y - e.y*u1)/(f.y + g.y*u1); } else { float w = k1*k1 - 4.0*k0*k2; if( w<0.0 ) return float2(-1.0, -1.0); w = sqrt( w ); v1 = (-k1 - w)/(2.0*k2); v2 = (-k1 + w)/(2.0*k2); u1 = (-k1u - w)/(2.0*k2u); u2 = (-k1u + w)/(2.0*k2u); } bool b1 = v1>0.0 && v1<1.0 && u1>0.0 && u1<1.0; bool b2 = v2>0.0 && v2<1.0 && u2>0.0 && u2<1.0; float2 res = float2(-1.0, -1.0); if( b2 ) return float2( u2, v2 ); if( b1 ) return float2( u1, v1 ); return float2(-1.0, -1.0); } float4 PassThrough(VertDataOut v_in) : TARGET { float2 xy = invBilinear(v_in.uv); float4 color = image.Sample(textureSampler, invBilinear(v_in.uv)); if (( xy.x == -1.0) || (xy.y == -1.0)) color.a = 0.0; return color; } technique Draw { pass { vertex_shader = VSDefault(v_in); pixel_shader = PassThrough(v_in); } } ]]