Last active
May 2, 2023 16:44
-
-
Save upgradeQ/2bcac29240a7449a80ee26a15936b984 to your computer and use it in GitHub Desktop.
Simple multi pass shaders for OBS Studio
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
S = obslua | |
local ffi = require"ffi" | |
local C = ffi.C | |
local _OR = bit.bor | |
ffi.cdef[[ | |
typedef struct {int x, y;} Point; | |
bool GetCursorPos(Point *lpPoint); | |
]] | |
RESOLUTION = {2560,1440}; | |
local mouse_pos = ffi.new("Point") | |
local function norm(val,amin,amax) return (val-amin ) / (amax- amin) end | |
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.current_time = 0 | |
instance.source = source | |
S.obs_enter_graphics() | |
instance.effect = S.gs_effect_create(SHADER, nil, nil) | |
instance.texture_a = S.gs_texrender_create(S.GS_RGBA,S.GS_ZS_NONE) | |
if instance.effect ~= nil then | |
instance.params = {} | |
instance.params.width = S.gs_effect_get_param_by_name(instance.effect, 'width') | |
instance.params.itime = S.gs_effect_get_param_by_name(instance.effect, 'itime') | |
instance.params.height = S.gs_effect_get_param_by_name(instance.effect, 'height') | |
instance.params.mouse_x = S.gs_effect_get_param_by_name(instance.effect, 'mouse_x') | |
instance.params.mouse_y = S.gs_effect_get_param_by_name(instance.effect, 'mouse_y') | |
instance.params.image = S.gs_effect_get_param_by_name(instance.effect, 'image') | |
end | |
S.obs_leave_graphics() | |
if instance.effect == nil then | |
SourceDef.destroy(instance) | |
return nil | |
end | |
return instance | |
end | |
function SourceDef:destroy() | |
if self.effect ~= nil then | |
S.obs_enter_graphics() | |
S.gs_effect_destroy(self.effect) | |
S.gs_texrender_destroy(self.texture_a) | |
S.obs_leave_graphics() | |
end | |
end | |
function SourceDef:get_name() return "Cursor Shader by upgradeQ1" 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 SourceDef:video_render() | |
C.GetCursorPos(mouse_pos) | |
local norm_x = norm(mouse_pos.x,0,RESOLUTION[1]) | |
local norm_y = norm(mouse_pos.y,0,RESOLUTION[2]) | |
S.gs_texrender_reset(self.texture_a) | |
S.gs_effect_set_float(self.params.itime, self.current_time+0.0) | |
S.gs_effect_set_float(self.params.mouse_x, norm_x+0.0) | |
S.gs_effect_set_float(self.params.mouse_y, norm_y+0.0) | |
S.gs_effect_set_int(self.params.width, self.width) | |
S.gs_effect_set_int(self.params.height, self.height) | |
if S.gs_texrender_begin(self.texture_a, self.width, self.height) then | |
local tex = S.gs_texrender_get_texture(self.texture_a) | |
S.gs_effect_set_texture(self.params.image,tex) | |
while S.gs_effect_loop(self.effect, "Draw") do | |
S.gs_draw_sprite(tex,0,self.width,self.height) | |
end | |
S.gs_texrender_end(self.texture_a) | |
end | |
S.gs_effect_set_float(self.params.itime, self.current_time + 0.0) | |
local tex2 = S.gs_texrender_get_texture(self.texture_a) | |
S.gs_effect_set_texture(self.params.image, tex2) | |
while S.gs_effect_loop(self.effect, "Draw2") do | |
S.gs_draw_sprite(tex2,0,self.width,self.height) | |
end | |
end | |
function script_load(settings) -- OBS_SOURCE_CUSTOM_DRAW | |
local my_filter = SourceDef:new({id='filter_cursor_shader1',type=S.OBS_SOURCE_TYPE_FILTER, | |
output_flags=_OR(S.OBS_SOURCE_VIDEO,S.OBS_SOURCE_CUSTOM_DRAW)}) | |
S.obs_register_source(my_filter) | |
end | |
SHADER = ([[ | |
// 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 image; | |
// Size of the source picture | |
uniform int width; | |
uniform int height; | |
uniform float mouse_x; | |
uniform float mouse_y; | |
uniform float itime; | |
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; | |
} | |
float4 lerp(float4 a, float4 b, float t) { return (a + t*(b-a)); } | |
float4 PassThrough(VertDataOut v_in) : TARGET | |
{ | |
float2 uv = v_in.uv; | |
float2 mousePos = float2(mouse_x,mouse_y); | |
float3 col = float3(0.902, 0.297, 0.234); | |
col = lerp(col, float3(0.898, 0.492, 0.133), smoothstep(0.0, 0.25, uv.x)); | |
col = lerp(col, float3(0.941, 0.766, 0.059), smoothstep(0.25, 0.50, uv.x)); | |
col = lerp(col, float3(0.180, 0.797, 0.441), smoothstep(0.50, 0.75, uv.x)); | |
col = lerp(col, float3(0.102, 0.734, 0.609), smoothstep(0.75, 1.00, uv.x)); | |
return float4(col,1.0); | |
} | |
float2 rotate(float2 st, float angle) { | |
return float2( | |
st.x * cos(angle) - st.y * sin(angle), | |
st.x * sin(angle) + st.y * cos(angle) | |
); | |
} | |
float atan_emu(float y, float x) | |
{ | |
if(x == 0 && y == 0) x = 1; | |
return atan2(y, x); | |
} | |
float4 PassThrough2(VertDataOut v_in) : TARGET | |
{ | |
float2 uv = v_in.uv; | |
uv = (2.0 *v_in.pos.xy - float2(2560,1440))/1440; | |
float ratio = %s.0/%s.0; | |
uv = rotate(uv - float2(.0,.0),itime); | |
uv = float2(-atan_emu(uv.y,uv.x) / 3.141459265 * 0.5 +0.5, 0.0); | |
float4 orig = image.Sample(textureSampler, uv); | |
return orig; | |
} | |
technique Draw | |
{ | |
pass | |
{ | |
vertex_shader = VSDefault(v_in); | |
pixel_shader = PassThrough(v_in); | |
} | |
} | |
technique Draw2 | |
{ | |
pass | |
{ | |
vertex_shader = VSDefault(v_in); | |
pixel_shader = PassThrough2(v_in); | |
} | |
} | |
]]):format(RESOLUTION[1],RESOLUTION[2], RESOLUTION[1],RESOLUTION[2]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
S = obslua | |
local ffi = require"ffi" | |
local C = ffi.C | |
local _OR = bit.bor | |
ffi.cdef[[ | |
typedef struct {int x, y;} Point; | |
bool GetCursorPos(Point *lpPoint); | |
]] | |
RESOLUTION = {2560,1440}; | |
local mouse_pos = ffi.new("Point") | |
local function norm(val,amin,amax) return (val-amin ) / (amax- amin) end | |
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.current_time = 0 | |
instance.source = source | |
S.obs_enter_graphics() | |
instance.effect = S.gs_effect_create(SHADER, nil, nil) | |
instance.texture_a = S.gs_texrender_create(S.GS_RGBA,S.GS_ZS_NONE) | |
instance.texture_b = S.gs_texrender_create(S.GS_RGBA,S.GS_ZS_NONE) | |
if instance.effect ~= nil then | |
instance.params = {} | |
instance.params.width = S.gs_effect_get_param_by_name(instance.effect, 'width') | |
instance.params.itime = S.gs_effect_get_param_by_name(instance.effect, 'itime') | |
instance.params.height = S.gs_effect_get_param_by_name(instance.effect, 'height') | |
instance.params.mouse_x = S.gs_effect_get_param_by_name(instance.effect, 'mouse_x') | |
instance.params.mouse_y = S.gs_effect_get_param_by_name(instance.effect, 'mouse_y') | |
instance.params.image = S.gs_effect_get_param_by_name(instance.effect, 'image') | |
end | |
S.obs_leave_graphics() | |
if instance.effect == nil then | |
SourceDef.destroy(instance) | |
return nil | |
end | |
return instance | |
end | |
function SourceDef:destroy() | |
if self.effect ~= nil then | |
S.obs_enter_graphics() | |
S.gs_effect_destroy(self.effect) | |
S.gs_texrender_destroy(self.texture_a) | |
S.gs_texrender_destroy(self.texture_b) | |
S.obs_leave_graphics() | |
end | |
end | |
function SourceDef:get_name() return "Cursor Shader by upgradeQ2" 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 SourceDef:video_render() | |
C.GetCursorPos(mouse_pos) | |
local norm_x = norm(mouse_pos.x,0,RESOLUTION[1]) | |
local norm_y = norm(mouse_pos.y,0,RESOLUTION[2]) | |
local target = S.obs_filter_get_target(self.source) | |
S.gs_texrender_reset(self.texture_a) | |
S.gs_effect_set_float(self.params.itime, self.current_time+0.0) | |
S.gs_effect_set_float(self.params.mouse_x, norm_x+0.0) | |
S.gs_effect_set_float(self.params.mouse_y, norm_y+0.0) | |
S.gs_effect_set_int(self.params.width, self.width) | |
S.gs_effect_set_int(self.params.height, self.height) | |
if S.gs_texrender_begin(self.texture_a, self.width, self.height) then | |
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.effect, self.width, self.height,"Draw") | |
S.gs_texrender_end(self.texture_a) | |
end | |
S.gs_effect_set_float(self.params.itime, self.current_time + 0.0) | |
S.gs_effect_set_float(self.params.mouse_x, norm_x+0.0) | |
S.gs_effect_set_float(self.params.mouse_y, norm_y+0.0) | |
S.gs_texrender_reset(self.texture_b) | |
if S.gs_texrender_begin(self.texture_b, self.width, self.height) then | |
local tex2 = S.gs_texrender_get_texture(self.texture_a) | |
S.gs_effect_set_texture(self.params.image, tex2) | |
while S.gs_effect_loop(self.effect, "Draw2") do | |
S.gs_draw_sprite(tex2,0,self.width,self.height) | |
end | |
S.gs_texrender_end(self.texture_b) | |
end | |
local tex3 = S.gs_texrender_get_texture(self.texture_b) | |
S.gs_effect_set_texture(self.params.image, tex3) | |
while S.gs_effect_loop(self.effect, "Draw3") do | |
S.gs_draw_sprite(tex3,0,self.width,self.height) | |
end | |
end | |
function script_load(settings) -- OBS_SOURCE_CUSTOM_DRAW | |
local my_filter = SourceDef:new({id='filter_cursor_shader2',type=S.OBS_SOURCE_TYPE_FILTER, | |
output_flags=_OR(S.OBS_SOURCE_VIDEO,S.OBS_SOURCE_CUSTOM_DRAW)}) | |
S.obs_register_source(my_filter) | |
end | |
SHADER = ([[ | |
// 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 image; | |
// Size of the source picture | |
uniform int width; | |
uniform int height; | |
uniform float mouse_x; | |
uniform float mouse_y; | |
uniform float itime; | |
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; | |
} | |
float4 lerp(float4 a, float4 b, float t) { return (a + t*(b-a)); } | |
float4 PassThrough(VertDataOut v_in) : TARGET | |
{ | |
float4 orig = image.Sample(textureSampler, v_in.uv); | |
//if (v_in.pos.x>0.5){return float4(0.5,0.5,0.5,1.0)} else {return float4(0.5,0.5,0.5,1.0);}; | |
//return float4(1.0,0.5,0.8,1.0); | |
return float4(1-orig.rgb,1.); | |
} | |
float4 PassThrough2(VertDataOut v_in) : TARGET | |
{ | |
float4 orig = image.Sample(textureSampler, v_in.uv); | |
if (v_in.uv.x<0.5){ | |
return float4(1.0,1.0,0.0,1.0);} else | |
{ | |
return float4(orig.rgb,1.); | |
} | |
} | |
float4 PassThrough3(VertDataOut v_in) : TARGET | |
{ | |
float4 orig = image.Sample(textureSampler, v_in.uv); | |
if (v_in.uv.y>0.1 && v_in.uv.y<0.5){ | |
return float4(1-orig.rgb,1.0); | |
} | |
return orig; | |
} | |
technique Draw | |
{ | |
pass | |
{ | |
vertex_shader = VSDefault(v_in); | |
pixel_shader = PassThrough(v_in); | |
} | |
} | |
technique Draw2 | |
{ | |
pass | |
{ | |
vertex_shader = VSDefault(v_in); | |
pixel_shader = PassThrough2(v_in); | |
} | |
} | |
technique Draw3 | |
{ | |
pass | |
{ | |
vertex_shader = VSDefault(v_in); | |
pixel_shader = PassThrough3(v_in); | |
} | |
} | |
]]):format(RESOLUTION[1],RESOLUTION[2], RESOLUTION[1],RESOLUTION[2]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
S = obslua | |
local ffi = require"ffi" | |
local C = ffi.C | |
local _OR = bit.bor | |
ffi.cdef[[ | |
typedef struct {int x, y;} Point; | |
bool GetCursorPos(Point *lpPoint); | |
]] | |
RESOLUTION = {2560,1440}; | |
local mouse_pos = ffi.new("Point") | |
local function norm(val,amin,amax) return (val-amin ) / (amax- amin) end | |
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.current_time = 0 | |
instance.source = source | |
S.obs_enter_graphics() | |
instance.effect = S.gs_effect_create(SHADER, nil, nil) | |
instance.texture_a = S.gs_texrender_create(S.GS_RGBA,S.GS_ZS_NONE) | |
instance.texture_b = S.gs_texrender_create(S.GS_RGBA,S.GS_ZS_NONE) | |
if instance.effect ~= nil then | |
instance.params = {} | |
instance.params.width = S.gs_effect_get_param_by_name(instance.effect, 'width') | |
instance.params.itime = S.gs_effect_get_param_by_name(instance.effect, 'itime') | |
instance.params.height = S.gs_effect_get_param_by_name(instance.effect, 'height') | |
instance.params.mouse_x = S.gs_effect_get_param_by_name(instance.effect, 'mouse_x') | |
instance.params.mouse_y = S.gs_effect_get_param_by_name(instance.effect, 'mouse_y') | |
instance.params.image = S.gs_effect_get_param_by_name(instance.effect, 'image') | |
end | |
S.obs_leave_graphics() | |
if instance.effect == nil then | |
SourceDef.destroy(instance) | |
return nil | |
end | |
return instance | |
end | |
function SourceDef:destroy() | |
if self.effect ~= nil then | |
S.obs_enter_graphics() | |
S.gs_effect_destroy(self.effect) | |
S.gs_texrender_destroy(self.texture_a) | |
S.gs_texrender_destroy(self.texture_b) | |
S.obs_leave_graphics() | |
end | |
end | |
function SourceDef:get_name() return "Cursor Shader by upgradeQ PORT mtSGDy" 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 SourceDef:video_render() | |
C.GetCursorPos(mouse_pos) | |
local _x = mouse_pos.x | |
local _y = mouse_pos.y | |
S.gs_texrender_reset(self.texture_a) | |
S.gs_effect_set_float(self.params.itime, self.current_time+0.0) | |
S.gs_effect_set_float(self.params.mouse_x, _x+0.0) | |
S.gs_effect_set_float(self.params.mouse_y, _y+0.0) | |
S.gs_effect_set_int(self.params.width, self.width) | |
S.gs_effect_set_int(self.params.height, self.height) | |
if S.gs_texrender_begin(self.texture_a, self.width, self.height) then | |
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.effect, self.width, self.height,"Draw") | |
S.gs_effect_set_texture(self.params.image, tex3) | |
while S.gs_effect_loop(self.effect, "Draw2") do | |
S.gs_draw_sprite(nil,0,self.width,self.height) -- nil?? | |
end | |
S.gs_texrender_end(self.texture_a) | |
end | |
S.gs_effect_set_float(self.params.itime, self.current_time + 0.0) | |
S.gs_effect_set_float(self.params.mouse_x, _x+0.0) | |
S.gs_effect_set_float(self.params.mouse_y, _y+0.0) | |
S.gs_texrender_reset(self.texture_b) | |
if S.gs_texrender_begin(self.texture_b, self.width, self.height) then | |
local tex2 = S.gs_texrender_get_texture(self.texture_a) | |
S.gs_effect_set_texture(self.params.image, tex2) | |
while S.gs_effect_loop(self.effect, "Draw") do | |
S.gs_draw_sprite(tex2,0,self.width,self.height) | |
end | |
S.gs_texrender_end(self.texture_b) | |
end | |
tex3 = S.gs_texrender_get_texture(self.texture_b) | |
S.gs_effect_set_texture(self.params.image, tex3) | |
while S.gs_effect_loop(self.effect, "Draw") do | |
S.gs_draw_sprite(tex3,0,self.width,self.height) | |
end | |
end | |
function script_load(settings) -- OBS_SOURCE_CUSTOM_DRAW | |
local my_filter = SourceDef:new({id='filter_cursor_shader_port',type=S.OBS_SOURCE_TYPE_FILTER, | |
output_flags=_OR(S.OBS_SOURCE_VIDEO,S.OBS_SOURCE_CUSTOM_DRAW)}) | |
S.obs_register_source(my_filter) | |
end | |
SHADER = ([[ | |
// 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 image; | |
// Size of the source picture | |
uniform int width; | |
uniform int height; | |
uniform float mouse_x; | |
uniform float mouse_y; | |
uniform float itime; | |
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; | |
} | |
float4 PassThrough(VertDataOut v_in) : TARGET | |
{ | |
float2 uv = v_in.uv; | |
float2 mousePos = float2(mouse_x,mouse_y); | |
float4 draw = image.Sample(textureSampler, uv); | |
float3 col = draw.xyz; | |
col = lerp(col, 1.0f.xxx, smoothstep(30.0f, 1.0f, length(v_in.pos.xyz - abs(mousePos.xy))).xxx); | |
col = clamp(col, 0.0f.xxx, 1.0f.xxx); | |
float4 fragColor = float4(-0.00999999977648258209228515625f, -0.00999999977648258209228515625f, -0.004999999888241291046142578125f, 1.0f) + float4(col, 1.0f); | |
return fragColor; | |
} | |
float4 PassThrough2(VertDataOut v_in) : TARGET | |
{ | |
float4 orig = image.Sample(textureSampler, v_in.uv); | |
return orig; | |
} | |
technique Draw | |
{ | |
pass | |
{ | |
vertex_shader = VSDefault(v_in); | |
pixel_shader = PassThrough(v_in); | |
} | |
} | |
technique Draw2 | |
{ | |
pass | |
{ | |
vertex_shader = VSDefault(v_in); | |
pixel_shader = PassThrough2(v_in); | |
} | |
} | |
]]):format(RESOLUTION[1],RESOLUTION[2], RESOLUTION[1],RESOLUTION[2]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
S = obslua | |
local ffi = require"ffi" | |
local C = ffi.C | |
local _OR = bit.bor | |
ffi.cdef[[ | |
typedef struct {int x, y;} Point; | |
bool GetCursorPos(Point *lpPoint); | |
]] | |
RESOLUTION = {2560,1440}; | |
local mouse_pos = ffi.new("Point") | |
local function norm(val,amin,amax) return (val-amin ) / (amax- amin) end | |
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.current_time = 0 | |
instance.source = source | |
S.obs_enter_graphics() | |
instance.effect = S.gs_effect_create(SHADER, nil, nil) | |
instance.texture_a = S.gs_texrender_create(S.GS_RGBA,S.GS_ZS_NONE) | |
instance.texture_b = S.gs_texrender_create(S.GS_RGBA,S.GS_ZS_NONE) | |
if instance.effect ~= nil then | |
instance.params = {} | |
instance.params.width = S.gs_effect_get_param_by_name(instance.effect, 'width') | |
instance.params.itime = S.gs_effect_get_param_by_name(instance.effect, 'itime') | |
instance.params.height = S.gs_effect_get_param_by_name(instance.effect, 'height') | |
instance.params.mouse_x = S.gs_effect_get_param_by_name(instance.effect, 'mouse_x') | |
instance.params.mouse_y = S.gs_effect_get_param_by_name(instance.effect, 'mouse_y') | |
instance.params.image = S.gs_effect_get_param_by_name(instance.effect, 'image') | |
end | |
S.obs_leave_graphics() | |
if instance.effect == nil then | |
SourceDef.destroy(instance) | |
return nil | |
end | |
return instance | |
end | |
function SourceDef:destroy() | |
if self.effect ~= nil then | |
S.obs_enter_graphics() | |
S.gs_effect_destroy(self.effect) | |
S.gs_texrender_destroy(self.texture_a) | |
S.gs_texrender_destroy(self.texture_b) | |
S.obs_leave_graphics() | |
end | |
end | |
function SourceDef:get_name() return "Cursor Shader by upgradeQ PORT WlsGRM" 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 SourceDef:video_render() | |
C.GetCursorPos(mouse_pos) | |
local _x = mouse_pos.x | |
local _y = mouse_pos.y | |
S.gs_texrender_reset(self.texture_a) | |
S.gs_effect_set_float(self.params.itime, self.current_time+0.0) | |
S.gs_effect_set_float(self.params.mouse_x, _x+0.0) | |
S.gs_effect_set_float(self.params.mouse_y, _y+0.0) | |
S.gs_effect_set_int(self.params.width, self.width) | |
S.gs_effect_set_int(self.params.height, self.height) | |
if S.gs_texrender_begin(self.texture_a, self.width, self.height) then | |
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.effect, self.width, self.height,"Draw") | |
S.gs_effect_set_texture(self.params.image, tex3) | |
while S.gs_effect_loop(self.effect, "Draw2") do | |
S.gs_draw_sprite(nil,0,self.width,self.height) -- nil?? | |
end | |
S.gs_texrender_end(self.texture_a) | |
end | |
S.gs_effect_set_float(self.params.itime, self.current_time + 0.0) | |
S.gs_effect_set_float(self.params.mouse_x, _x+0.0) | |
S.gs_effect_set_float(self.params.mouse_y, _y+0.0) | |
S.gs_texrender_reset(self.texture_b) | |
if S.gs_texrender_begin(self.texture_b, self.width, self.height) then | |
local tex2 = S.gs_texrender_get_texture(self.texture_a) | |
S.gs_effect_set_texture(self.params.image, tex2) | |
while S.gs_effect_loop(self.effect, "Draw") do | |
S.gs_draw_sprite(tex2,0,self.width,self.height) | |
end | |
S.gs_texrender_end(self.texture_b) | |
end | |
tex3 = S.gs_texrender_get_texture(self.texture_b) | |
S.gs_effect_set_texture(self.params.image, tex3) | |
while S.gs_effect_loop(self.effect, "Draw") do | |
S.gs_draw_sprite(tex3,0,self.width,self.height) | |
end | |
end | |
function script_load(settings) -- OBS_SOURCE_CUSTOM_DRAW | |
local my_filter = SourceDef:new({id='filter_cursor_shader_port',type=S.OBS_SOURCE_TYPE_FILTER, | |
output_flags=_OR(S.OBS_SOURCE_VIDEO,S.OBS_SOURCE_CUSTOM_DRAW)}) | |
S.obs_register_source(my_filter) | |
end | |
SHADER = ([[ | |
// 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 image; | |
// Size of the source picture | |
uniform int width; | |
uniform int height; | |
uniform float mouse_x; | |
uniform float mouse_y; | |
uniform float itime; | |
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; | |
} | |
float4 PassThrough(VertDataOut v_in) : TARGET | |
{ | |
float2 uv = v_in.uv; | |
float2 mousePos = float2(mouse_x,mouse_y); | |
float4 draw = image.Sample(textureSampler, uv); | |
float3 blob = (0.10999999940395355224609375f - clamp(length((v_in.pos.xyz - mousePos) / width.xx), 0.0f, 0.10999999940395355224609375f)).xxx * 2.0f; | |
float3 stack = draw.xyz * float3(0.9900000095367431640625f, 0.98199999332427978515625f, 0.930000007152557373046875f); | |
float4 fragColor = float4(stack + blob, 1.0f); | |
return fragColor; | |
} | |
float4 PassThrough2(VertDataOut v_in) : TARGET | |
{ | |
float4 orig = image.Sample(textureSampler, v_in.uv); | |
return orig; | |
} | |
technique Draw | |
{ | |
pass | |
{ | |
vertex_shader = VSDefault(v_in); | |
pixel_shader = PassThrough(v_in); | |
} | |
} | |
technique Draw2 | |
{ | |
pass | |
{ | |
vertex_shader = VSDefault(v_in); | |
pixel_shader = PassThrough2(v_in); | |
} | |
} | |
]]):format(RESOLUTION[1],RESOLUTION[2], RESOLUTION[1],RESOLUTION[2]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment