Skip to content

Instantly share code, notes, and snippets.

@cowboy
Last active September 7, 2021 01:30
Show Gist options
  • Save cowboy/9b33aed126b78f60ca8c126567625fc9 to your computer and use it in GitHub Desktop.
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
// ===============================================================
// 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