Last active
September 7, 2021 01:30
-
-
Save cowboy/9b33aed126b78f60ca8c126567625fc9 to your computer and use it in GitHub Desktop.
Draw a border around a source - For use as a filter with StreamFX in OBS Studio
This file contains hidden or 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
// =============================================================== | |
// Draw a border around a source | |
// For use as a filter with StreamFX in OBS Studio | |
// =============================================================== | |
// "Cowboy" Ben Alman - 2021 | |
// https://gist.github.com/cowboy/9b33aed126b78f60ca8c126567625fc9 | |
// =============================================================== | |
// See https://github.com/Xaymar/obs-StreamFX/wiki/Source-Filter-Transition-Shader | |
uniform float4x4 ViewProj< | |
bool automatic = true; | |
>; | |
uniform float4 ViewSize< | |
bool automatic = true; | |
>; | |
uniform float4 Time< | |
bool automatic = true; | |
>; | |
uniform float4x4 Random< | |
bool automatic = true; | |
>; | |
uniform texture2d InputA< | |
bool automatic = true; | |
>; | |
// =============================================================== | |
// User editable parameters | |
// =============================================================== | |
// Should the source content be shrunk to fit inside the borders, | |
// or should it stay as-is with the border overlapping it? | |
uniform int inset< | |
bool visible = true; | |
string name = "Border Mode"; | |
string field_type = "enum"; | |
int enum_0 = 1; | |
string enum_0_name = "Fit source inside border"; | |
int enum_1 = 0; | |
string enum_1_name = "Border overlaps source"; | |
> = 1; | |
// Allow scaling the border in case you're working with a source | |
// that has been scaled - however, instead of changing this param, | |
// it's recommended to put the filter on a group that contains the | |
// source. This also allows the filter to work with cropped sources. | |
uniform float size< | |
string name = "Border Size"; | |
string field_type = "slider"; | |
float minimum = 0.; | |
float maximum = 20.; | |
float step = 0.5; | |
float scale = 1.; | |
> = 4.0; | |
// =============================================================== | |
// Shader Code | |
// =============================================================== | |
sampler_state def_sampler { | |
AddressU = Clamp; | |
AddressV = Clamp; | |
Filter = Linear; | |
}; | |
struct VertData { | |
float4 pos : POSITION; | |
float2 uv : TEXCOORD0; | |
}; | |
// I have no idea what a vertex shader is, but this seems | |
// to be required | |
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; | |
} | |
// Draw border like this around the edge of a source | |
// where # = black and * = white, eg. | |
// | |
// ##################### | |
// #*******************# | |
// #*#################*# | |
// #*#ORIGINAL_SOURCE#*# | |
// #*#ORIGINAL_SOURCE#*# | |
// #*#ORIGINAL_SOURCE#*# | |
// #*#################*# | |
// #*******************# | |
// ##################### | |
bool is_between(float val, float min_val, float max_val) { | |
return val > min_val && val < max_val; | |
} | |
float4 PSDefault(VertData v_in) : TARGET { | |
// For debugging (this looks hilarious with multiple sources on the screen) | |
// float size = (sin(Time * 4) + 1) * 20; | |
// Border colors | |
float4 inner_border_color = float4(1.0, 1.0, 1.0, 1.0); // White | |
float4 outer_border_color = float4(0.0, 0.0, 0.0, 1.0); // Black | |
// Pixel coordinates (pre-transform/crop) | |
float x = v_in.pos.x; | |
float y = v_in.pos.y; | |
// Viewport size (pre-transform/crop) | |
float w = ViewSize.xy.x; | |
float h = ViewSize.xy.y; | |
// Border sizes - Use size, size*2.0, size*3.0 if you want | |
// the border widths to be exactly the same. | |
float boundary_1 = size * 1.2; | |
float boundary_2 = size * 2.1; | |
float boundary_3 = size * 3.2; | |
// Draw middle border, eg "*" from above diagram | |
bool is_within_y, is_within_x; | |
if ( | |
(is_between(x, boundary_1, boundary_2) && (is_within_y = is_between(y, boundary_1, h - boundary_1))) || | |
(is_between(y, boundary_1, boundary_2) && (is_within_x = is_between(x, boundary_1, w - boundary_1))) || | |
(is_between(x, w - boundary_2, w - boundary_1) && is_within_y) || | |
(is_between(y, h - boundary_2, h - boundary_1) && is_within_x) | |
) { | |
return inner_border_color; | |
} | |
// Otherwise draw outer borders, eg "#" from above diagram | |
if (!is_between(x, boundary_3, w - boundary_3) || !is_between(y, boundary_3, h - boundary_3)) { | |
return outer_border_color; | |
} | |
// Fill color for debugging | |
// return float4(0.0, 1.0, 0.0, 1.0); | |
// Scale the source to fit within the border as closely as possible | |
if (inset) { | |
float x_scale = w / (w - 2.0 * boundary_3); | |
float y_scale = h / (h - 2.0 * boundary_3); | |
float scale = x_scale; | |
float2 offset_scaler = {1.0, 1.0}; | |
if (x_scale < y_scale) { | |
offset_scaler.y *= 1.5; | |
} else if (x_scale > y_scale) { | |
scale = y_scale; | |
offset_scaler.x *= 1.5; | |
} | |
float2 offset = {boundary_3 / w * scale, boundary_3 / h * scale}; | |
v_in.uv = v_in.uv * scale - (offset / offset_scaler); | |
} | |
// Otherwise just return the current pixel | |
return InputA.Sample(def_sampler, v_in.uv); | |
} | |
technique Draw | |
{ | |
pass | |
{ | |
vertex_shader = VSDefault(v_in); | |
pixel_shader = PSDefault(v_in); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment