Skip to content

Instantly share code, notes, and snippets.

@couleurm
Forked from upgradeQ/test_frame_blending.lua
Last active October 7, 2024 19:36
Show Gist options
  • Save couleurm/2ae41b68f9a2e4c093ecddb65827f511 to your computer and use it in GitHub Desktop.
Save couleurm/2ae41b68f9a2e4c093ecddb65827f511 to your computer and use it in GitHub Desktop.
frame_blend
S = obslua
samples = 360 / 60
local function skip_tick_render(ctx)
local target = S.obs_filter_get_target(ctx.source)
local width, height;
if target == nil then width = 0; height = 0; else
width = S.obs_source_get_base_width(target)
height = S.obs_source_get_base_height(target)
end
ctx.width, ctx.height = width , height
end
local SourceDef = {}
function SourceDef:new(o)
o = o or {}
setmetatable(o, self)
self.__index = self
return o
end
function SourceDef:create(source)
local instance = {}
instance.width = 1
instance.height = 1
instance.source = source
instance.current_time = 0
instance.frame_number = 0
instance.strength = 0.001
for i=1, samples do
instance["texture" .. tostring(i)] = S.gs_texrender_create(S.GS_RGBA, S.GS_ZS_NONE)
end
instance.clear_flags = bit.bor(S.GS_CLEAR_COLOR)
instance.current_texrender = S.gs_texrender_create(S.GS_RGBA, S.GS_ZS_NONE)
instance.texture_clear_color = S.vec4()
instance.clear_flags = bit.bor(S.GS_CLEAR_COLOR)
S.obs_enter_graphics()
instance.effect = S.gs_effect_create(SHADER1, nil, nil)
if instance.effect ~= nil then
instance.params = {}
instance.params.itime = S.gs_effect_get_param_by_name(instance.effect, 'itime')
instance.params.strength = S.gs_effect_get_param_by_name(instance.effect, 'strength')
for i=1, samples do
instance.params["tex" .. tostring(i)] = S.gs_effect_get_param_by_name(instance.effect, "tex" .. tostring(i))
end
instance.params.current_frame = S.gs_effect_get_param_by_name(instance.effect, 'current_frame')
end
instance.default_effect = S.obs_get_base_effect(S.OBS_EFFECT_DEFAULT)
instance.params.default_image = S.gs_effect_get_param_by_name(instance.default_effect, 'image')
instance.params.itime = S.gs_effect_get_param_by_name(instance.effect, 'itime')
S.obs_leave_graphics()
if instance.effect == nil then
SourceDef.destroy(instance)
return nil
end
--SourceDef.update(instance,self) -- initialize, self = settings
return instance
end
function SourceDef:destroy()
if self.effect ~= nil then
S.obs_enter_graphics()
for i=1, samples do
S.gs_texrender_destroy(self["texture" .. tostring(i)])
end
S.gs_texrender_destroy(self.current_texrender)
S.gs_effect_destroy(self.effect)
S.obs_leave_graphics()
end
end
function SourceDef:get_name() return "frame blending ?" end
function SourceDef:get_width() return self.width end
function SourceDef:get_height() return self.height end
function SourceDef:video_tick(seconds)
self.current_time = self.current_time + seconds
skip_tick_render(self) -- if source has crop or transform applied to it, this will let it render
end
function texrender_set_texture(self, texrender, texture)
S.gs_texrender_reset(texrender)
if S.gs_texrender_begin(texrender, self.width, self.height) then
S.gs_ortho(0, self.width, 0, self.height, -100.0, 100.0)
S.gs_effect_set_texture(self.params.default_image, texture)
while S.gs_effect_loop(self.default_effect, "Draw") do
S.gs_draw_sprite(nil, 0, self.width, self.height)
end
S.gs_texrender_end(texrender)
end
end
function SourceDef:video_render()
local parent = S.obs_filter_get_parent(self.source)
self.width = S.obs_source_get_base_width(parent)
self.height = S.obs_source_get_base_height(parent)
S.gs_texrender_reset(self.current_texrender)
if S.gs_texrender_begin(self.current_texrender, self.width, self.height) then
S.gs_ortho(0, self.width, 0, self.height, -100.0, 100.0)
--S.gs_clear(self.clear_flags, self.texture_clear_color, 0, 0)
S.gs_effect_set_float(self.params.itime, self.current_time+0.0)
if not S.obs_source_process_filter_begin(self.source, S.GS_RGBA, S.OBS_ALLOW_DIRECT_RENDERING)
then return
end
S.obs_source_process_filter_tech_end(self.source, self.default_effect, self.width, self.height,"Draw")
S.gs_texrender_end(self.current_texrender)
end
local switch = self.frame_number % samples
texrender_set_texture(self, self["texture" .. tostring(switch+1)], S.gs_texrender_get_texture(self.current_texrender))
for i=1, samples do
S.gs_effect_set_texture(self.params["tex" .. tostring(i)], S.gs_texrender_get_texture(self["texture" .. tostring(i)]))
end
S.gs_effect_set_texture(self.params.current_frame, S.gs_texrender_get_texture(self.current_texrender))
S.gs_effect_set_float(self.params.strength, self.strength)
while S.gs_effect_loop(self.effect, "DrawFinal") do
S.gs_draw_sprite(nil ,0,self.width,self.height)
end
self.frame_number = self.frame_number + 1
end
function SourceDef:get_properties()
local props = S.obs_properties_create()
S.obs_properties_add_float_slider(props, "_h", "strength", 0, 1, 0.00001)
return props
end
function SourceDef:update(settings)
self.strength = S.obs_data_get_double(settings, "_h")
end
function SourceDef:get_defaults()
S.obs_data_set_default_double(self, "_h", 0.01)
end
function script_properties()
local props = S.obs_properties_create()
S.obs_properties_add_button(props, "button2", "frame blending ? ",
function() end)
return props
end
function script_load(settings)
local my_filter = SourceDef:new({id='frame_blend',type=S.OBS_SOURCE_TYPE_FILTER,
output_flags=bit.bor(S.OBS_SOURCE_VIDEO,S.OBS_SOURCE_CUSTOM_DRAW)})
S.obs_register_source(my_filter)
end
Tex = ""
for i=1, samples do
Tex = Tex .. "uniform Texture2D tex" .. i .. ";\n"
end
PassThrough1 = "float4 color = tex" .. samples .. ".Sample(textureSampler, uv);\n"
-- float4 color = tex6.Sample(textureSampler, uv);
for i=(samples-1), 1, -1 do
PassThrough1 = PassThrough1 .. "color = (color + tex" .. i .. ".Sample(textureSampler, uv)) / 2.0;\n"
-- -- color = (color + tex1.Sample(textureSampler, uv)) / 2.0;
end
PassThrough = "float4 color = tex" .. samples .. ".Sample(textureSampler, uv);\n"
for i=(samples-1), 1, -1 do
PassThrough = PassThrough .. "color = (color + tex" .. i .. ".Sample(textureSampler, uv));\n"
-- -- color = (color + tex1.Sample(textureSampler, uv)) / 2.0;
end
print(Tex)
SHADER1 = string.format([[
// OBS-specific syntax adaptation to HLSL standard to avoid errors reported by the code editor
#define SamplerState sampler_state
#define Texture2D texture2d
uniform float4x4 ViewProj;
uniform Texture2D current_frame;
%s
// Size of the source picture
uniform float itime;
uniform float strength;
SamplerState 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;
}
// https://github.com/libretro/common-shaders/blob/master/motionblur/shaders/motionblur-simple.cg
float4 PassThrough1(VertDataOut v_in) : TARGET
{
float2 uv = v_in.uv;
float4 draw = current_frame.Sample(textureSampler, uv);
%s
color = (color + draw) / 2.0;
return color;
}
// https://reshade.me/forum/shader-discussion/2312-who-said-reshade-cant-do-quality-motion-blur
float4 PassThrough(VertDataOut v_in) : TARGET
{
float2 uv = v_in.uv;
float4 draw = current_frame.Sample(textureSampler, uv);
%s
color = (color + draw) / %d;
return lerp(draw,color,strength);
}
technique DrawFinal
{
pass
{
vertex_shader = VSDefault(v_in);
pixel_shader = PassThrough(v_in);
}
}
]], Tex, PassThrough1, PassThrough, samples+1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment