obs-StreamFX/data/effects/sdf/sdf-producer.effect
Michael Fabian 'Xaymar' Dirks 5a3954ae0e project: Fix License, License headers and Copyright information
Fixes several files incorrectly stated a different license from the actual project, as well as the copyright headers included in all files. This change has no effect on the licensing terms, it should clear up a bit of confusion by contributors. Plus the files get a bit smaller, and we have less duplicated information across the entire project.

Overall the project is GPLv2 if not built with Qt, and GPLv3 if it is built with Qt. There are no parts licensed under a different license, all have been adapted from other compatible licenses into GPLv2 or GPLv3.
2023-04-05 18:59:08 +02:00

230 lines
5.4 KiB
Text

// AUTOGENERATED COPYRIGHT HEADER START
// Copyright (C) 2019-2023 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
// AUTOGENERATED COPYRIGHT HEADER END
// 2D Signed Distance Field Generator
//
// This will produce an approximated Signed Distance Field on the fly.
// Version 1.0:
// - Inputs:
// - _image: Source Image
// - _size: Size of SDF Frame
// - _sdf: Last SDF Frame
// - _threshold: Alpha Threshold
// - Output:
// - float4
// - R: If outside, distance to nearest wall, otherwise 0.
// - G: If inside, distance to nearest wall, otherwise 0.
// - BA: UV coordinates of nearest wall.
//
// Version 1.1:
// - See Version 1.0
// - Adjusted R, G to be 0..1 range, multiply by 65536.0 to get proper results.
// -------------------------------------------------------------------------------- //
// Defines
#define MAX_DISTANCE 65536.0
#define NEAR_INFINITE 18446744073709551616.0
#define RANGE 4
// -------------------------------------------------------------------------------- //
// OBS Default
uniform float4x4 ViewProj;
// Inputs
uniform texture2d _image;
uniform float2 _size;
uniform texture2d _sdf; // in, out - swap rendering
uniform float _threshold;
sampler_state sdfSampler {
Filter = Point;
AddressU = Clamp;
AddressV = Clamp;
};
sampler_state sdfSampler1_1 {
Filter = Linear;
AddressU = Border;
AddressV = Border;
BorderColor = FFFFFFFF;
};
sampler_state imageSampler {
Filter = Point;
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 PS_SDFGenerator_v1(VertDataOut v_in) : TARGET
{
float4 outval = float4(0.0, 0.0, v_in.uv.x, v_in.uv.y);
// utility values
float2 uv_step = 1.0 / _size;
float lowest = NEAR_INFINITE;
float2 lowest_source = float2(NEAR_INFINITE, NEAR_INFINITE);
float2 lowest_origin = float2(NEAR_INFINITE, NEAR_INFINITE);
// inputs
float imageA = _image.Sample(imageSampler, v_in.uv).a;
// sdf contains 4 values: R = Positive Distance, G = Negative Distance, BA = UV of nearest edge.
if (imageA > _threshold) {
// Inside
// TODO: Optimize to be O(n*n) instead of (2n*2n)
for (int x = -RANGE; x < RANGE; x++) {
for (int y = -RANGE; y < RANGE; y++) {
if ((x == 0) && (y == 0)) {
continue;
}
float2 dtr = float2(x, y);
float2 dt = uv_step * dtr;
float4 here = _sdf.Sample(sdfSampler, v_in.uv + dt);
float dst = abs(distance(float2(0., 0.), dtr));
if (lowest > (here.g + dst)) {
lowest = here.g + dst;
lowest_source = v_in.uv + dt;
lowest_origin = here.ba;
}
}
}
if (lowest < NEAR_INFINITE) {
outval.g = lowest;
outval.ba = lowest_origin;
}
} else {
// Outside
// TODO: Optimize to be O(n*n) instead of (2n*2n)
for (int x = -RANGE; x < RANGE; x++) {
for (int y = -RANGE; y < RANGE; y++) {
if ((x == 0) && (y == 0)) {
continue;
}
float2 dtr = float2(x, y);
float2 dt = uv_step * dtr;
float4 here = _sdf.Sample(sdfSampler, v_in.uv + dt);
float dst = abs(distance(float2(0., 0.), dtr));
if (lowest > (here.r + dst)) {
lowest = here.r + dst;
lowest_source = v_in.uv + dt;
lowest_origin = here.ba;
}
}
}
if (lowest < NEAR_INFINITE) {
outval.r = lowest;
outval.ba = lowest_origin;
}
}
return outval;
}
float4 PS_SDFGenerator_v1_1(VertDataOut v_in) : TARGET
{
const float step = 1.0 / MAX_DISTANCE;
float4 outval = float4(0.0, 0.0, v_in.uv.x, v_in.uv.y);
// utility values
float2 uv_step = 1.0 / _size;
float lowest = NEAR_INFINITE;
float2 lowest_source = float2(NEAR_INFINITE, NEAR_INFINITE);
float2 lowest_origin = float2(NEAR_INFINITE, NEAR_INFINITE);
// inputs
float imageA = _image.Sample(imageSampler, v_in.uv).a;
float4 self = _sdf.Sample(sdfSampler1_1, v_in.uv);
if (imageA > _threshold) {
// Inside
// TODO: Optimize to be O(n*n) instead of (2n*2n)
for (int x = -RANGE; x < RANGE; x++) {
for (int y = -RANGE; y < RANGE; y++) {
if ((x == 0) && (y == 0)) {
continue;
}
float2 dtr = float2(x, y);
float2 dt = uv_step * dtr;
float4 here = _sdf.Sample(sdfSampler1_1, v_in.uv + dt);
float dst = abs(distance(float2(0., 0.), dtr)) * step;
if (lowest > (here.g + dst)) {
lowest = here.g + dst;
lowest_source = v_in.uv + dt;
lowest_origin = here.ba;
}
}
}
if (lowest < NEAR_INFINITE) {
outval.g = lowest;
outval.ba = lowest_origin;
} else {
outval.g = self.g + step;
}
} else {
// Outside
// TODO: Optimize to be O(n*n) instead of (2n*2n)
for (int x = -RANGE; x < RANGE; x++) {
for (int y = -RANGE; y < RANGE; y++) {
if ((x == 0) && (y == 0)) {
continue;
}
float2 dtr = float2(x, y);
float2 dt = uv_step * dtr;
float4 here = _sdf.Sample(sdfSampler1_1, v_in.uv + dt);
float dst = abs(distance(float2(0., 0.), dtr)) * step;
if (lowest > (here.r + dst)) {
lowest = here.r + dst;
lowest_source = v_in.uv + dt;
lowest_origin = here.ba;
}
}
}
if (lowest < NEAR_INFINITE) {
outval.r = lowest;
outval.ba = lowest_origin;
} else {
outval.r = self.r + step;
}
}
return outval;
}
technique Draw
{
pass
{
vertex_shader = VSDefault(v_in);
pixel_shader = PS_SDFGenerator_v1_1(v_in);
}
}