mirror of
https://github.com/Xaymar/obs-StreamFX
synced 2024-11-11 06:15:05 +00:00
effects, filter-blur: Precalculated Kernel, Formatting, Rendering and more
Filter: * Massively improved Rendering loop to reduce overhead and provide cleaner code. Gaussian Blur: * Added precalculated Kernels using Textures. Slightly reduced GPU load. * Added basic attempt at a Nvidia o(n/2+1) gaussian blur implementation. * Fix up LOD for texture sampling. Box Blur: * Halved loop count.
This commit is contained in:
parent
374d1183ea
commit
4dbf414214
5 changed files with 450 additions and 329 deletions
|
@ -1,8 +1,15 @@
|
||||||
|
// OBS Default
|
||||||
uniform float4x4 ViewProj;
|
uniform float4x4 ViewProj;
|
||||||
uniform texture2d image;
|
|
||||||
uniform float2 texel;
|
// Settings (Shared)
|
||||||
uniform int widthHalf;
|
uniform texture2d u_image;
|
||||||
uniform int width;
|
uniform float2 u_imageSize;
|
||||||
|
uniform float2 u_imageTexelDelta;
|
||||||
|
uniform int u_radius;
|
||||||
|
uniform int u_diameter;
|
||||||
|
uniform float2 u_texelDelta;
|
||||||
|
|
||||||
|
// Settings (Private)
|
||||||
uniform float bilateralSmoothing;
|
uniform float bilateralSmoothing;
|
||||||
uniform float bilateralSharpness;
|
uniform float bilateralSharpness;
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
|
// OBS Default
|
||||||
uniform float4x4 ViewProj;
|
uniform float4x4 ViewProj;
|
||||||
uniform texture2d image;
|
|
||||||
uniform float2 texel;
|
// Settings (Shared)
|
||||||
uniform int widthHalf;
|
uniform texture2d u_image;
|
||||||
uniform int width;
|
uniform float2 u_imageSize;
|
||||||
|
uniform float2 u_imageTexelDelta;
|
||||||
|
uniform int u_radius;
|
||||||
|
uniform int u_diameter;
|
||||||
|
uniform float2 u_texelDelta;
|
||||||
|
|
||||||
sampler_state textureSampler {
|
sampler_state textureSampler {
|
||||||
Filter = Point;
|
Filter = Point;
|
||||||
|
@ -31,11 +36,12 @@ VertDataOut VSDefault(VertDataIn v_in)
|
||||||
// Box Blur
|
// Box Blur
|
||||||
float4 PSBox(VertDataOut v_in) : TARGET
|
float4 PSBox(VertDataOut v_in) : TARGET
|
||||||
{
|
{
|
||||||
float4 rgba = float4(0,0,0,0);
|
float4 rgba = u_image.Sample(textureSampler, v_in.uv);
|
||||||
for (int k = -widthHalf; k <= widthHalf; k++) {
|
for (int k = 1; k <= u_radius; k++) {
|
||||||
rgba += image.Sample(textureSampler, v_in.uv + (texel * k));
|
rgba += u_image.Sample(textureSampler, v_in.uv + (u_texelDelta * k));
|
||||||
|
rgba += u_image.Sample(textureSampler, v_in.uv - (u_texelDelta * k));
|
||||||
}
|
}
|
||||||
rgba = rgba / width;
|
rgba = rgba / u_diameter;
|
||||||
return rgba;
|
return rgba;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,32 @@
|
||||||
|
// OBS Default
|
||||||
uniform float4x4 ViewProj;
|
uniform float4x4 ViewProj;
|
||||||
uniform texture2d image;
|
|
||||||
uniform float2 texel;
|
|
||||||
uniform int widthHalf;
|
|
||||||
uniform int width;
|
|
||||||
|
|
||||||
sampler_state textureSampler {
|
// Settings (Shared)
|
||||||
|
uniform texture2d u_image;
|
||||||
|
uniform float2 u_imageSize;
|
||||||
|
uniform float2 u_imageTexelDelta;
|
||||||
|
uniform int u_radius;
|
||||||
|
uniform int u_diameter;
|
||||||
|
uniform float2 u_texelDelta;
|
||||||
|
|
||||||
|
// Settings (Private)
|
||||||
|
//uniform float registerkernel[25];
|
||||||
|
uniform texture2d kernel;
|
||||||
|
uniform float2 kernelTexel;
|
||||||
|
|
||||||
|
sampler_state pointClampSampler {
|
||||||
Filter = Point;
|
Filter = Point;
|
||||||
AddressU = Clamp;
|
AddressU = Clamp;
|
||||||
AddressV = Clamp;
|
AddressV = Clamp;
|
||||||
|
MinLOD = 0;
|
||||||
|
MaxLOD = 0;
|
||||||
|
};
|
||||||
|
sampler_state bilinearClampSampler {
|
||||||
|
Filter = Bilinear;
|
||||||
|
AddressU = Clamp;
|
||||||
|
AddressV = Clamp;
|
||||||
|
MinLOD = 0;
|
||||||
|
MaxLOD = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VertDataIn {
|
struct VertDataIn {
|
||||||
|
@ -34,19 +53,69 @@ float Gaussian(float x, float o) {
|
||||||
return (1.0 / (o * sqrt(2.0 * pivalue))) * exp((-(x * x)) / (2 * (o * o)));
|
return (1.0 / (o * sqrt(2.0 * pivalue))) * exp((-(x * x)) / (2 * (o * o)));
|
||||||
}
|
}
|
||||||
|
|
||||||
float4 PSGaussian(VertDataOut v_in) : TARGET {
|
float4 InternalGaussian(
|
||||||
const float pivalue = 3.1415926535897932384626433832795;
|
float2 p_uv, float2 p_uvStep, int p_size,
|
||||||
|
texture2d p_source) {
|
||||||
float4 rgba = image.Sample(textureSampler, v_in.uv) * Gaussian(0, width / pivalue);
|
float l_gauss = Gaussian(0, p_size);
|
||||||
float2 uvoffset = texel;
|
float4 l_value = p_source.Sample(pointClampSampler, p_uv) * gauss;
|
||||||
for (int k = 1; k <= widthHalf; k++) {
|
float2 l_uvoffset = p_uvStep;
|
||||||
float g = Gaussian(k, width / pivalue);
|
for (int k = 1; k <= p_size; k++) {
|
||||||
float4 spos = image.Sample(textureSampler, v_in.uv + uvoffset) * g;
|
float l_g = Gaussian(k, p_size);
|
||||||
float4 sneg = image.Sample(textureSampler, v_in.uv - uvoffset) * g;
|
float4 l_p = p_source.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g;
|
||||||
rgba += spos + sneg;
|
float4 l_n = p_source.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g;
|
||||||
uvoffset += texel;
|
l_value += l_p + l_n;
|
||||||
|
l_uvoffset += p_uvStep;
|
||||||
|
l_gauss += l_g;
|
||||||
}
|
}
|
||||||
return rgba;
|
l_value = l_value * (1.0 / l_gauss);
|
||||||
|
return l_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 InternalGaussianPrecalculated(
|
||||||
|
float2 p_uv, float2 p_uvStep, int p_size,
|
||||||
|
texture2d p_source, float2 p_sourceStep,
|
||||||
|
texture2d p_kernel, float2 p_kernelStep) {
|
||||||
|
float4 l_value = p_source.Sample(pointClampSampler, p_uv + imageTexel)
|
||||||
|
* kernel.Sample(pointClampSampler, float2(0, 0)).r;
|
||||||
|
float2 l_uvoffset = p_uvStep;
|
||||||
|
float2 l_koffset = p_kerneltexel;
|
||||||
|
for (int k = 1; k <= p_size; k++) {
|
||||||
|
float l_g = p_kernel.Sample(pointClampSampler, l_koffset).r;
|
||||||
|
float4 l_p = p_source.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g;
|
||||||
|
float4 l_n = p_source.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g;
|
||||||
|
l_value += l_p + l_n;
|
||||||
|
l_uvoffset += p_uvStep;
|
||||||
|
l_koffset += p_kerneltexel;
|
||||||
|
}
|
||||||
|
return l_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 InternalGaussianPrecalculatedNVOptimized(float2 p_uv, texture2d p_source, float2 p_texel,
|
||||||
|
int p_size, texture2d p_kernel, texture2d p_kerneltexel) {
|
||||||
|
if (p_size % 2 == 0) {
|
||||||
|
float4 l_value = p_source.Sample(pointClampSampler, p_uv)
|
||||||
|
* kernel.Sample(pointClampSampler, float2(0, 0)).r;
|
||||||
|
float2 l_uvoffset = p_texel;
|
||||||
|
float2 l_koffset = p_kerneltexel;
|
||||||
|
for (int k = 1; k <= p_size; k++) {
|
||||||
|
float l_g = p_kernel.Sample(pointClampSampler, l_koffset).r;
|
||||||
|
float4 l_p = p_source.Sample(pointClampSampler, p_uv + l_uvoffset) * l_g;
|
||||||
|
float4 l_n = p_source.Sample(pointClampSampler, p_uv - l_uvoffset) * l_g;
|
||||||
|
l_value += l_p + l_n;
|
||||||
|
l_uvoffset += p_texel;
|
||||||
|
l_koffset += p_kerneltexel;
|
||||||
|
}
|
||||||
|
return l_value;
|
||||||
|
} else {
|
||||||
|
return InternalGaussianPrecalculated(p_uv, p_source, p_texel, p_size, p_kernel, p_kerneltexel);)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 PSGaussian(VertDataOut v_in) : TARGET {
|
||||||
|
//return InternalGaussian(v_in.uv, u_image, u_imageTexelDelta, u_radius);
|
||||||
|
//return InternalGaussianPrecalculated(v_in.uv, image, texel, radius, kernel, kernelTexel);
|
||||||
|
return InternalGaussianPrecalculatedNVOptimize(v_in.uv, u_
|
||||||
|
image, texel, radius, kernel, kernelTexel);
|
||||||
}
|
}
|
||||||
|
|
||||||
technique Draw
|
technique Draw
|
||||||
|
|
|
@ -46,13 +46,64 @@ static gs_effect_t* g_boxBlurEffect, *g_colorConversionEffect;
|
||||||
|
|
||||||
static size_t g_maxKernelSize = 25;
|
static size_t g_maxKernelSize = 25;
|
||||||
|
|
||||||
|
const double_t pi = 3.1415926535897932384626433832795;
|
||||||
|
const double_t twopi = 6.283185307179586476925286766559;
|
||||||
|
const double_t twopisqr = 2.506628274631000502415765284811;
|
||||||
|
const double_t half = 0.5;
|
||||||
|
|
||||||
double_t gaussian1D(double_t x, double_t o) {
|
double_t gaussian1D(double_t x, double_t o) {
|
||||||
return (1.0 / (o * sqrt(2 * M_PI))) * exp(-(x*x) / (2 * (o*o)));
|
double_t c = (x / o);
|
||||||
|
double_t b = exp(-half * c * c);
|
||||||
|
double_t a = (1.0 / (o * twopisqr));
|
||||||
|
return a * b;
|
||||||
}
|
}
|
||||||
double_t bilateral(double_t x, double_t o) {
|
double_t bilateral(double_t x, double_t o) {
|
||||||
return 0.39894 * exp(-0.5 * (x * x) / (o * o)) / o;
|
return 0.39894 * exp(-0.5 * (x * x) / (o * o)) / o;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void makeGaussianKernels() {
|
||||||
|
g_gaussianBlur.kernels.resize(g_maxKernelSize);
|
||||||
|
|
||||||
|
std::vector<float_t> textureBuffer;
|
||||||
|
std::vector<double_t> mathBuffer;
|
||||||
|
|
||||||
|
for (size_t n = 1; n <= g_maxKernelSize; n++) {
|
||||||
|
textureBuffer.resize(n * 4);
|
||||||
|
mathBuffer.resize(n);
|
||||||
|
|
||||||
|
// Calculate and normalize
|
||||||
|
double_t sum = 0.0;
|
||||||
|
for (size_t p = 0; p < n; p++) {
|
||||||
|
double_t g = gaussian1D(double_t(p), double_t(n));
|
||||||
|
mathBuffer[p] = g;
|
||||||
|
sum += g;
|
||||||
|
if (p != 0)
|
||||||
|
sum += g;
|
||||||
|
}
|
||||||
|
for (size_t p = 0; p < n; p++) {
|
||||||
|
mathBuffer[p] /= sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build Texture
|
||||||
|
for (size_t p = 0; p < n; p++) {
|
||||||
|
textureBuffer[p * 4] =
|
||||||
|
textureBuffer[p * 4 + 1] =
|
||||||
|
textureBuffer[p * 4 + 2] =
|
||||||
|
textureBuffer[p * 4 + 3] = (float_t)mathBuffer[p];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* data = reinterpret_cast<uint8_t*>(textureBuffer.data());
|
||||||
|
const uint8_t** pdata = const_cast<const uint8_t**>(&data);
|
||||||
|
|
||||||
|
gs_texture_t* tex = gs_texture_create(uint32_t(n), 1, gs_color_format::GS_RGBA32F, 1, pdata, 0);
|
||||||
|
if (!tex) {
|
||||||
|
P_LOG_ERROR("<filter-blur> Failed to create gaussian kernel for %d width.", n);
|
||||||
|
} else {
|
||||||
|
g_gaussianBlur.kernels[n - 1] = tex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Filter::Blur::Blur() {
|
Filter::Blur::Blur() {
|
||||||
memset(&sourceInfo, 0, sizeof(obs_source_info));
|
memset(&sourceInfo, 0, sizeof(obs_source_info));
|
||||||
sourceInfo.id = "obs-stream-effects-filter-blur";
|
sourceInfo.id = "obs-stream-effects-filter-blur";
|
||||||
|
@ -82,10 +133,10 @@ Filter::Blur::Blur() {
|
||||||
g_boxBlurEffect = gs_effect_create_from_file(file, &loadError);
|
g_boxBlurEffect = gs_effect_create_from_file(file, &loadError);
|
||||||
bfree(file);
|
bfree(file);
|
||||||
if (loadError != nullptr) {
|
if (loadError != nullptr) {
|
||||||
PLOG_ERROR("<filter-blur> Loading box-blur effect failed with error(s): %s", loadError);
|
P_LOG_ERROR("<filter-blur> Loading box-blur effect failed with error(s): %s", loadError);
|
||||||
bfree(loadError);
|
bfree(loadError);
|
||||||
} else if (!g_boxBlurEffect) {
|
} else if (!g_boxBlurEffect) {
|
||||||
PLOG_ERROR("<filter-blur> Loading box-blur effect failed with unspecified error.");
|
P_LOG_ERROR("<filter-blur> Loading box-blur effect failed with unspecified error.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Gaussian Blur
|
/// Gaussian Blur
|
||||||
|
@ -96,38 +147,13 @@ Filter::Blur::Blur() {
|
||||||
effect = gs_effect_create_from_file(file, &loadError);
|
effect = gs_effect_create_from_file(file, &loadError);
|
||||||
bfree(file);
|
bfree(file);
|
||||||
if (loadError != nullptr) {
|
if (loadError != nullptr) {
|
||||||
PLOG_ERROR("<filter-blur> Loading gaussian blur effect failed with error(s): %s", loadError);
|
P_LOG_ERROR("<filter-blur> Loading gaussian blur effect failed with error(s): %s", loadError);
|
||||||
bfree(loadError);
|
bfree(loadError);
|
||||||
} else if (!effect) {
|
} else if (!effect) {
|
||||||
PLOG_ERROR("<filter-blur> Loading gaussian blur effect failed with unspecified error.");
|
P_LOG_ERROR("<filter-blur> Loading gaussian blur effect failed with unspecified error.");
|
||||||
} else {
|
} else {
|
||||||
g_gaussianBlur.effect = effect;
|
g_gaussianBlur.effect = effect;
|
||||||
g_gaussianBlur.kernels.resize(g_maxKernelSize);
|
|
||||||
std::vector<float_t> databuf;
|
|
||||||
for (size_t n = 1; n <= g_maxKernelSize; n++) {
|
|
||||||
databuf.resize(n);
|
|
||||||
// Calculate
|
|
||||||
double_t sum = 0.0;
|
|
||||||
for (size_t p = 0; p < n; p ++) {
|
|
||||||
databuf[p] = gaussian1D(p, n);
|
|
||||||
sum += databuf[p];
|
|
||||||
if (p != 0)
|
|
||||||
sum += databuf[p];
|
|
||||||
}
|
|
||||||
// Normalize
|
|
||||||
for (size_t p = 0; p < n; p++) {
|
|
||||||
databuf[p] /= sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* data = reinterpret_cast<uint8_t*>(databuf.data());
|
|
||||||
const uint8_t** pdata = const_cast<const uint8_t**>(&data);
|
|
||||||
gs_texture_t* tex = gs_texture_create(n, 1, gs_color_format::GS_R32F, 1, pdata, 0);
|
|
||||||
if (!tex) {
|
|
||||||
PLOG_ERROR("<filter-blur> Failed to create gaussian kernel for %d width.", n);
|
|
||||||
} else {
|
|
||||||
g_gaussianBlur.kernels[n - 1] = tex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Bilateral Blur
|
/// Bilateral Blur
|
||||||
|
@ -138,38 +164,13 @@ Filter::Blur::Blur() {
|
||||||
effect = gs_effect_create_from_file(file, &loadError);
|
effect = gs_effect_create_from_file(file, &loadError);
|
||||||
bfree(file);
|
bfree(file);
|
||||||
if (loadError != nullptr) {
|
if (loadError != nullptr) {
|
||||||
PLOG_ERROR("<filter-blur> Loading bilateral blur effect failed with error(s): %s", loadError);
|
P_LOG_ERROR("<filter-blur> Loading bilateral blur effect failed with error(s): %s", loadError);
|
||||||
bfree(loadError);
|
bfree(loadError);
|
||||||
} else if (!effect) {
|
} else if (!effect) {
|
||||||
PLOG_ERROR("<filter-blur> Loading bilateral blur effect failed with unspecified error.");
|
P_LOG_ERROR("<filter-blur> Loading bilateral blur effect failed with unspecified error.");
|
||||||
} else {
|
} else {
|
||||||
g_bilateralBlur.effect = effect;
|
g_bilateralBlur.effect = effect;
|
||||||
g_bilateralBlur.kernels.resize(g_maxKernelSize);
|
makeGaussianKernels();
|
||||||
std::vector<float_t> databuf;
|
|
||||||
for (size_t n = 1; n <= g_maxKernelSize; n++) {
|
|
||||||
databuf.resize(n);
|
|
||||||
// Calculate
|
|
||||||
double_t sum = 0.0;
|
|
||||||
for (size_t p = 0; p < n; p++) {
|
|
||||||
databuf[p] = gaussian1D(p, M_PI);
|
|
||||||
sum += databuf[p];
|
|
||||||
if (p != 0)
|
|
||||||
sum += databuf[p];
|
|
||||||
}
|
|
||||||
// Normalize
|
|
||||||
for (size_t p = 0; p < n; p++) {
|
|
||||||
databuf[p] /= sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* data = reinterpret_cast<uint8_t*>(databuf.data());
|
|
||||||
const uint8_t** pdata = const_cast<const uint8_t**>(&data);
|
|
||||||
gs_texture_t* tex = gs_texture_create(n, 1, gs_color_format::GS_R32F, 1, pdata, 0);
|
|
||||||
if (!tex) {
|
|
||||||
PLOG_ERROR("<filter-blur> Failed to create bilateral kernel for %d width.", n);
|
|
||||||
} else {
|
|
||||||
g_bilateralBlur.kernels[n - 1] = tex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,10 +181,10 @@ Filter::Blur::Blur() {
|
||||||
g_colorConversionEffect = gs_effect_create_from_file(file, &loadError);
|
g_colorConversionEffect = gs_effect_create_from_file(file, &loadError);
|
||||||
bfree(file);
|
bfree(file);
|
||||||
if (loadError != nullptr) {
|
if (loadError != nullptr) {
|
||||||
PLOG_ERROR("<filter-blur> Loading color conversion effect failed with error(s): %s", loadError);
|
P_LOG_ERROR("<filter-blur> Loading color conversion effect failed with error(s): %s", loadError);
|
||||||
bfree(loadError);
|
bfree(loadError);
|
||||||
} else if (!g_colorConversionEffect) {
|
} else if (!g_colorConversionEffect) {
|
||||||
PLOG_ERROR("<filter-blur> Loading color conversion effect failed with unspecified error.");
|
P_LOG_ERROR("<filter-blur> Loading color conversion effect failed with unspecified error.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,67 +200,68 @@ Filter::Blur::~Blur() {
|
||||||
gs_effect_destroy(g_bilateralBlur.effect);
|
gs_effect_destroy(g_bilateralBlur.effect);
|
||||||
gs_effect_destroy(g_gaussianBlur.effect);
|
gs_effect_destroy(g_gaussianBlur.effect);
|
||||||
for (size_t n = 1; n <= g_maxKernelSize; n++) {
|
for (size_t n = 1; n <= g_maxKernelSize; n++) {
|
||||||
gs_texture_destroy(g_gaussianBlur.kernels[n - 1]);
|
if (g_gaussianBlur.kernels.size() > 0)
|
||||||
|
gs_texture_destroy(g_gaussianBlur.kernels[n - 1]);
|
||||||
}
|
}
|
||||||
gs_effect_destroy(g_boxBlurEffect);
|
gs_effect_destroy(g_boxBlurEffect);
|
||||||
obs_leave_graphics();
|
obs_leave_graphics();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char * Filter::Blur::get_name(void *) {
|
const char * Filter::Blur::get_name(void *) {
|
||||||
return P_TRANSLATE(P_FILTER_BLUR);
|
return P_TRANSLATE(S_FILTER_BLUR);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Filter::Blur::get_defaults(obs_data_t *data) {
|
void Filter::Blur::get_defaults(obs_data_t *data) {
|
||||||
obs_data_set_default_int(data, P_FILTER_BLUR_TYPE, Filter::Blur::Type::Box);
|
obs_data_set_default_int(data, S_FILTER_BLUR_TYPE, Filter::Blur::Type::Box);
|
||||||
obs_data_set_default_int(data, P_FILTER_BLUR_SIZE, 5);
|
obs_data_set_default_int(data, S_FILTER_BLUR_SIZE, 5);
|
||||||
|
|
||||||
// Bilateral Only
|
// Bilateral Only
|
||||||
obs_data_set_default_double(data, P_FILTER_BLUR_BILATERAL_SMOOTHING, 50.0);
|
obs_data_set_default_double(data, S_FILTER_BLUR_BILATERAL_SMOOTHING, 50.0);
|
||||||
obs_data_set_default_double(data, P_FILTER_BLUR_BILATERAL_SHARPNESS, 90.0);
|
obs_data_set_default_double(data, S_FILTER_BLUR_BILATERAL_SHARPNESS, 90.0);
|
||||||
|
|
||||||
// Advanced
|
// Advanced
|
||||||
obs_data_set_default_bool(data, P_FILTER_BLUR_ADVANCED, false);
|
obs_data_set_default_bool(data, S_ADVANCED, false);
|
||||||
obs_data_set_default_int(data, P_FILTER_BLUR_ADVANCED_COLORFORMAT, ColorFormat::RGB);
|
obs_data_set_default_int(data, S_FILTER_BLUR_COLORFORMAT, ColorFormat::RGB);
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_properties_t * Filter::Blur::get_properties(void *) {
|
obs_properties_t * Filter::Blur::get_properties(void *) {
|
||||||
obs_properties_t *pr = obs_properties_create();
|
obs_properties_t *pr = obs_properties_create();
|
||||||
obs_property_t* p = NULL;
|
obs_property_t* p = NULL;
|
||||||
|
|
||||||
p = obs_properties_add_list(pr, P_FILTER_BLUR_TYPE, P_TRANSLATE(P_FILTER_BLUR_TYPE),
|
p = obs_properties_add_list(pr, S_FILTER_BLUR_TYPE, P_TRANSLATE(S_FILTER_BLUR_TYPE),
|
||||||
obs_combo_type::OBS_COMBO_TYPE_LIST, obs_combo_format::OBS_COMBO_FORMAT_INT);
|
obs_combo_type::OBS_COMBO_TYPE_LIST, obs_combo_format::OBS_COMBO_FORMAT_INT);
|
||||||
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_FILTER_BLUR_TYPE)));
|
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(S_FILTER_BLUR_TYPE)));
|
||||||
obs_property_set_modified_callback(p, modified_properties);
|
obs_property_set_modified_callback(p, modified_properties);
|
||||||
obs_property_list_add_int(p, P_TRANSLATE(P_FILTER_BLUR_TYPE_BOX),
|
obs_property_list_add_int(p, P_TRANSLATE(S_FILTER_BLUR_TYPE_BOX),
|
||||||
Filter::Blur::Type::Box);
|
Filter::Blur::Type::Box);
|
||||||
obs_property_list_add_int(p, P_TRANSLATE(P_FILTER_BLUR_TYPE_GAUSSIAN),
|
obs_property_list_add_int(p, P_TRANSLATE(S_FILTER_BLUR_TYPE_GAUSSIAN),
|
||||||
Filter::Blur::Type::Gaussian);
|
Filter::Blur::Type::Gaussian);
|
||||||
obs_property_list_add_int(p, P_TRANSLATE(P_FILTER_BLUR_TYPE_BILATERAL),
|
obs_property_list_add_int(p, P_TRANSLATE(S_FILTER_BLUR_TYPE_BILATERAL),
|
||||||
Filter::Blur::Type::Bilateral);
|
Filter::Blur::Type::Bilateral);
|
||||||
|
|
||||||
p = obs_properties_add_int_slider(pr, P_FILTER_BLUR_SIZE,
|
p = obs_properties_add_int_slider(pr, S_FILTER_BLUR_SIZE,
|
||||||
P_TRANSLATE(P_FILTER_BLUR_SIZE), 1, 25, 1);
|
P_TRANSLATE(S_FILTER_BLUR_SIZE), 1, 25, 1);
|
||||||
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_FILTER_BLUR_SIZE)));
|
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(S_FILTER_BLUR_SIZE)));
|
||||||
obs_property_set_modified_callback(p, modified_properties);
|
obs_property_set_modified_callback(p, modified_properties);
|
||||||
|
|
||||||
// Bilateral Only
|
// Bilateral Only
|
||||||
p = obs_properties_add_float_slider(pr, P_FILTER_BLUR_BILATERAL_SMOOTHING,
|
p = obs_properties_add_float_slider(pr, S_FILTER_BLUR_BILATERAL_SMOOTHING,
|
||||||
P_TRANSLATE(P_FILTER_BLUR_BILATERAL_SMOOTHING), 0.01, 100.0, 0.01);
|
P_TRANSLATE(S_FILTER_BLUR_BILATERAL_SMOOTHING), 0.01, 100.0, 0.01);
|
||||||
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_FILTER_BLUR_BILATERAL_SMOOTHING)));
|
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(S_FILTER_BLUR_BILATERAL_SMOOTHING)));
|
||||||
p = obs_properties_add_float_slider(pr, P_FILTER_BLUR_BILATERAL_SHARPNESS,
|
p = obs_properties_add_float_slider(pr, S_FILTER_BLUR_BILATERAL_SHARPNESS,
|
||||||
P_TRANSLATE(P_FILTER_BLUR_BILATERAL_SHARPNESS), 0, 99.99, 0.01);
|
P_TRANSLATE(S_FILTER_BLUR_BILATERAL_SHARPNESS), 0, 99.99, 0.01);
|
||||||
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_FILTER_BLUR_BILATERAL_SHARPNESS)));
|
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(S_FILTER_BLUR_BILATERAL_SHARPNESS)));
|
||||||
|
|
||||||
// Advanced
|
// Advanced
|
||||||
p = obs_properties_add_bool(pr, P_FILTER_BLUR_ADVANCED, P_TRANSLATE(P_FILTER_BLUR_ADVANCED));
|
p = obs_properties_add_bool(pr, S_ADVANCED, P_TRANSLATE(S_ADVANCED));
|
||||||
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_FILTER_BLUR_ADVANCED)));
|
obs_property_set_long_description(p, P_TRANSLATE(P_DESC(S_ADVANCED)));
|
||||||
obs_property_set_modified_callback(p, modified_properties);
|
obs_property_set_modified_callback(p, modified_properties);
|
||||||
|
|
||||||
p = obs_properties_add_list(pr, P_FILTER_BLUR_ADVANCED_COLORFORMAT,
|
p = obs_properties_add_list(pr, S_FILTER_BLUR_COLORFORMAT,
|
||||||
P_TRANSLATE(P_FILTER_BLUR_ADVANCED_COLORFORMAT),
|
P_TRANSLATE(S_FILTER_BLUR_COLORFORMAT),
|
||||||
obs_combo_type::OBS_COMBO_TYPE_LIST, obs_combo_format::OBS_COMBO_FORMAT_INT);
|
obs_combo_type::OBS_COMBO_TYPE_LIST, obs_combo_format::OBS_COMBO_FORMAT_INT);
|
||||||
obs_property_set_long_description(p,
|
obs_property_set_long_description(p,
|
||||||
P_TRANSLATE(P_DESC(P_FILTER_BLUR_ADVANCED_COLORFORMAT)));
|
P_TRANSLATE(P_DESC(S_FILTER_BLUR_COLORFORMAT)));
|
||||||
obs_property_list_add_int(p, "RGB",
|
obs_property_list_add_int(p, "RGB",
|
||||||
ColorFormat::RGB);
|
ColorFormat::RGB);
|
||||||
obs_property_list_add_int(p, "YUV",
|
obs_property_list_add_int(p, "YUV",
|
||||||
|
@ -271,7 +273,7 @@ obs_properties_t * Filter::Blur::get_properties(void *) {
|
||||||
bool Filter::Blur::modified_properties(obs_properties_t *pr, obs_property_t *, obs_data_t *d) {
|
bool Filter::Blur::modified_properties(obs_properties_t *pr, obs_property_t *, obs_data_t *d) {
|
||||||
bool showBilateral = false;
|
bool showBilateral = false;
|
||||||
|
|
||||||
switch (obs_data_get_int(d, P_FILTER_BLUR_TYPE)) {
|
switch (obs_data_get_int(d, S_FILTER_BLUR_TYPE)) {
|
||||||
case Filter::Blur::Type::Box:
|
case Filter::Blur::Type::Box:
|
||||||
break;
|
break;
|
||||||
case Filter::Blur::Type::Gaussian:
|
case Filter::Blur::Type::Gaussian:
|
||||||
|
@ -282,17 +284,17 @@ bool Filter::Blur::modified_properties(obs_properties_t *pr, obs_property_t *, o
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bilateral Blur
|
// Bilateral Blur
|
||||||
obs_property_set_visible(obs_properties_get(pr, P_FILTER_BLUR_BILATERAL_SMOOTHING),
|
obs_property_set_visible(obs_properties_get(pr, S_FILTER_BLUR_BILATERAL_SMOOTHING),
|
||||||
showBilateral);
|
showBilateral);
|
||||||
obs_property_set_visible(obs_properties_get(pr, P_FILTER_BLUR_BILATERAL_SHARPNESS),
|
obs_property_set_visible(obs_properties_get(pr, S_FILTER_BLUR_BILATERAL_SHARPNESS),
|
||||||
showBilateral);
|
showBilateral);
|
||||||
|
|
||||||
// Advanced
|
// Advanced
|
||||||
bool showAdvanced = false;
|
bool showAdvanced = false;
|
||||||
if (obs_data_get_bool(d, P_FILTER_BLUR_ADVANCED))
|
if (obs_data_get_bool(d, S_ADVANCED))
|
||||||
showAdvanced = true;
|
showAdvanced = true;
|
||||||
|
|
||||||
obs_property_set_visible(obs_properties_get(pr, P_FILTER_BLUR_ADVANCED_COLORFORMAT),
|
obs_property_set_visible(obs_properties_get(pr, S_FILTER_BLUR_COLORFORMAT),
|
||||||
showAdvanced);
|
showAdvanced);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -364,7 +366,7 @@ Filter::Blur::Instance::~Instance() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Filter::Blur::Instance::update(obs_data_t *data) {
|
void Filter::Blur::Instance::update(obs_data_t *data) {
|
||||||
m_type = (Type)obs_data_get_int(data, P_FILTER_BLUR_TYPE);
|
m_type = (Type)obs_data_get_int(data, S_FILTER_BLUR_TYPE);
|
||||||
switch (m_type) {
|
switch (m_type) {
|
||||||
case Filter::Blur::Type::Box:
|
case Filter::Blur::Type::Box:
|
||||||
m_effect = g_boxBlurEffect;
|
m_effect = g_boxBlurEffect;
|
||||||
|
@ -376,14 +378,14 @@ void Filter::Blur::Instance::update(obs_data_t *data) {
|
||||||
m_effect = g_bilateralBlur.effect;
|
m_effect = g_bilateralBlur.effect;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
m_size = (uint64_t)obs_data_get_int(data, P_FILTER_BLUR_SIZE);
|
m_size = (uint64_t)obs_data_get_int(data, S_FILTER_BLUR_SIZE);
|
||||||
|
|
||||||
// Bilateral Blur
|
// Bilateral Blur
|
||||||
m_bilateralSmoothing = obs_data_get_double(data, P_FILTER_BLUR_BILATERAL_SMOOTHING) / 100.0;
|
m_bilateralSmoothing = obs_data_get_double(data, S_FILTER_BLUR_BILATERAL_SMOOTHING) / 100.0;
|
||||||
m_bilateralSharpness = obs_data_get_double(data, P_FILTER_BLUR_BILATERAL_SHARPNESS) / 100.0;
|
m_bilateralSharpness = obs_data_get_double(data, S_FILTER_BLUR_BILATERAL_SHARPNESS) / 100.0;
|
||||||
|
|
||||||
// Advanced
|
// Advanced
|
||||||
m_colorFormat = obs_data_get_int(data, P_FILTER_BLUR_ADVANCED_COLORFORMAT);
|
m_colorFormat = obs_data_get_int(data, S_FILTER_BLUR_COLORFORMAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t Filter::Blur::Instance::get_width() {
|
uint32_t Filter::Blur::Instance::get_width() {
|
||||||
|
@ -406,6 +408,7 @@ void Filter::Blur::Instance::video_tick(float) {}
|
||||||
|
|
||||||
void Filter::Blur::Instance::video_render(gs_effect_t *effect) {
|
void Filter::Blur::Instance::video_render(gs_effect_t *effect) {
|
||||||
bool failed = false;
|
bool failed = false;
|
||||||
|
vec4 black; vec4_zero(&black);
|
||||||
obs_source_t
|
obs_source_t
|
||||||
*parent = obs_filter_get_parent(m_source),
|
*parent = obs_filter_get_parent(m_source),
|
||||||
*target = obs_filter_get_target(m_source);
|
*target = obs_filter_get_target(m_source);
|
||||||
|
@ -426,7 +429,7 @@ void Filter::Blur::Instance::video_render(gs_effect_t *effect) {
|
||||||
#pragma region Source To Texture
|
#pragma region Source To Texture
|
||||||
gs_texrender_reset(m_primaryRT);
|
gs_texrender_reset(m_primaryRT);
|
||||||
if (!gs_texrender_begin(m_primaryRT, baseW, baseH)) {
|
if (!gs_texrender_begin(m_primaryRT, baseW, baseH)) {
|
||||||
PLOG_ERROR("<filter-blur> Failed to set up base texture.");
|
P_LOG_ERROR("<filter-blur> Failed to set up base texture.");
|
||||||
obs_source_skip_video_filter(m_source);
|
obs_source_skip_video_filter(m_source);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
@ -440,11 +443,8 @@ void Filter::Blur::Instance::video_render(gs_effect_t *effect) {
|
||||||
// Set up camera stuff
|
// Set up camera stuff
|
||||||
gs_set_cull_mode(GS_NEITHER);
|
gs_set_cull_mode(GS_NEITHER);
|
||||||
gs_reset_blend_state();
|
gs_reset_blend_state();
|
||||||
gs_blend_function_separate(
|
gs_enable_blending(false);
|
||||||
gs_blend_type::GS_BLEND_ONE,
|
gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
|
||||||
gs_blend_type::GS_BLEND_ZERO,
|
|
||||||
gs_blend_type::GS_BLEND_ONE,
|
|
||||||
gs_blend_type::GS_BLEND_ZERO);
|
|
||||||
gs_enable_depth_test(false);
|
gs_enable_depth_test(false);
|
||||||
gs_enable_stencil_test(false);
|
gs_enable_stencil_test(false);
|
||||||
gs_enable_stencil_write(false);
|
gs_enable_stencil_write(false);
|
||||||
|
@ -454,7 +454,7 @@ void Filter::Blur::Instance::video_render(gs_effect_t *effect) {
|
||||||
if (obs_source_process_filter_begin(m_source, GS_RGBA, OBS_NO_DIRECT_RENDERING)) {
|
if (obs_source_process_filter_begin(m_source, GS_RGBA, OBS_NO_DIRECT_RENDERING)) {
|
||||||
obs_source_process_filter_end(m_source, effect ? effect : defaultEffect, baseW, baseH);
|
obs_source_process_filter_end(m_source, effect ? effect : defaultEffect, baseW, baseH);
|
||||||
} else {
|
} else {
|
||||||
PLOG_ERROR("<filter-blur> Unable to render source.");
|
P_LOG_ERROR("<filter-blur> Unable to render source.");
|
||||||
failed = true;
|
failed = true;
|
||||||
}
|
}
|
||||||
gs_texrender_end(m_primaryRT);
|
gs_texrender_end(m_primaryRT);
|
||||||
|
@ -467,35 +467,31 @@ void Filter::Blur::Instance::video_render(gs_effect_t *effect) {
|
||||||
|
|
||||||
sourceTexture = gs_texrender_get_texture(m_primaryRT);
|
sourceTexture = gs_texrender_get_texture(m_primaryRT);
|
||||||
if (!sourceTexture) {
|
if (!sourceTexture) {
|
||||||
PLOG_ERROR("<filter-blur> Failed to get source texture.");
|
P_LOG_ERROR("<filter-blur> Failed to get source texture.");
|
||||||
obs_source_skip_video_filter(m_source);
|
obs_source_skip_video_filter(m_source);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#pragma endregion Source To Texture
|
#pragma endregion Source To Texture
|
||||||
|
|
||||||
// Conversion
|
// Conversion
|
||||||
|
#pragma region RGB -> YUV
|
||||||
if (m_colorFormat == ColorFormat::YUV) {
|
if (m_colorFormat == ColorFormat::YUV) {
|
||||||
gs_texrender_reset(m_secondaryRT);
|
gs_texrender_reset(m_secondaryRT);
|
||||||
if (!gs_texrender_begin(m_secondaryRT, baseW, baseH)) {
|
if (!gs_texrender_begin(m_secondaryRT, baseW, baseH)) {
|
||||||
PLOG_ERROR("<filter-blur> Failed to set up base texture.");
|
P_LOG_ERROR("<filter-blur> Failed to set up base texture.");
|
||||||
obs_source_skip_video_filter(m_source);
|
obs_source_skip_video_filter(m_source);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
gs_ortho(0, (float)baseW, 0, (float)baseH, -1, 1);
|
gs_ortho(0, (float)baseW, 0, (float)baseH, -1, 1);
|
||||||
|
|
||||||
// Clear to Black
|
// Clear to Black
|
||||||
vec4 black;
|
|
||||||
vec4_zero(&black);
|
|
||||||
gs_clear(GS_CLEAR_COLOR | GS_CLEAR_DEPTH, &black, 0, 0);
|
gs_clear(GS_CLEAR_COLOR | GS_CLEAR_DEPTH, &black, 0, 0);
|
||||||
|
|
||||||
// Set up camera stuff
|
// Set up camera stuff
|
||||||
gs_set_cull_mode(GS_NEITHER);
|
gs_set_cull_mode(GS_NEITHER);
|
||||||
gs_reset_blend_state();
|
gs_reset_blend_state();
|
||||||
gs_blend_function_separate(
|
gs_enable_blending(false);
|
||||||
gs_blend_type::GS_BLEND_ONE,
|
gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
|
||||||
gs_blend_type::GS_BLEND_ZERO,
|
|
||||||
gs_blend_type::GS_BLEND_ONE,
|
|
||||||
gs_blend_type::GS_BLEND_ZERO);
|
|
||||||
gs_enable_depth_test(false);
|
gs_enable_depth_test(false);
|
||||||
gs_enable_stencil_test(false);
|
gs_enable_stencil_test(false);
|
||||||
gs_enable_stencil_write(false);
|
gs_enable_stencil_write(false);
|
||||||
|
@ -503,7 +499,7 @@ void Filter::Blur::Instance::video_render(gs_effect_t *effect) {
|
||||||
|
|
||||||
gs_eparam_t* param = gs_effect_get_param_by_name(g_colorConversionEffect, "image");
|
gs_eparam_t* param = gs_effect_get_param_by_name(g_colorConversionEffect, "image");
|
||||||
if (!param) {
|
if (!param) {
|
||||||
PLOG_ERROR("<filter-blur:Final> Failed to set image param.");
|
P_LOG_ERROR("<filter-blur:Final> Failed to set image param.");
|
||||||
failed = true;
|
failed = true;
|
||||||
} else {
|
} else {
|
||||||
gs_effect_set_texture(param, sourceTexture);
|
gs_effect_set_texture(param, sourceTexture);
|
||||||
|
@ -521,18 +517,77 @@ void Filter::Blur::Instance::video_render(gs_effect_t *effect) {
|
||||||
|
|
||||||
sourceTexture = gs_texrender_get_texture(m_secondaryRT);
|
sourceTexture = gs_texrender_get_texture(m_secondaryRT);
|
||||||
if (!sourceTexture) {
|
if (!sourceTexture) {
|
||||||
PLOG_ERROR("<filter-blur> Failed to get source texture.");
|
P_LOG_ERROR("<filter-blur> Failed to get source texture.");
|
||||||
obs_source_skip_video_filter(m_source);
|
obs_source_skip_video_filter(m_source);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#pragma endregion RGB -> YUV
|
||||||
|
|
||||||
gs_texture_t *blurred = blur_render(sourceTexture, baseW, baseH);
|
#pragma region Blur
|
||||||
|
// Set up camera stuff
|
||||||
|
gs_set_cull_mode(GS_NEITHER);
|
||||||
|
gs_reset_blend_state();
|
||||||
|
gs_enable_blending(true);
|
||||||
|
gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
|
||||||
|
gs_enable_depth_test(false);
|
||||||
|
gs_enable_stencil_test(false);
|
||||||
|
gs_enable_stencil_write(false);
|
||||||
|
gs_enable_color(true, true, true, true);
|
||||||
|
|
||||||
|
gs_texture_t* blurred = nullptr, *intermediate = sourceTexture;
|
||||||
|
std::tuple<const char*, gs_texrender_t*, float, float> kvs[] = {
|
||||||
|
std::make_tuple("Horizontal", m_rtHorizontal, 1.0f / baseW, 0.0f),
|
||||||
|
std::make_tuple("Vertical", m_rtVertical, 0.0f, 1.0f / baseH),
|
||||||
|
};
|
||||||
|
for (auto v : kvs) {
|
||||||
|
const char* name = std::get<0>(v);
|
||||||
|
gs_texrender_t* rt = std::get<1>(v);
|
||||||
|
float xpel = std::get<2>(v),
|
||||||
|
ypel = std::get<3>(v);
|
||||||
|
|
||||||
|
if (!apply_shared_param(intermediate, xpel, ypel))
|
||||||
|
break;
|
||||||
|
switch (m_type) {
|
||||||
|
case Gaussian:
|
||||||
|
apply_gaussian_param();
|
||||||
|
break;
|
||||||
|
case Bilateral:
|
||||||
|
apply_bilateral_param();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
gs_texrender_reset(rt);
|
||||||
|
if (!gs_texrender_begin(rt, baseW, baseH)) {
|
||||||
|
P_LOG_ERROR("<filter-blur:%s> Failed to begin rendering.", name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Camera
|
||||||
|
gs_ortho(0, (float)baseW, 0, (float)baseH, -1, 1);
|
||||||
|
gs_clear(GS_CLEAR_COLOR | GS_CLEAR_DEPTH, &black, 0, 0);
|
||||||
|
|
||||||
|
// Render
|
||||||
|
while (gs_effect_loop(m_effect, "Draw")) {
|
||||||
|
gs_draw_sprite(intermediate, 0, baseW, baseH);
|
||||||
|
}
|
||||||
|
|
||||||
|
gs_texrender_end(rt);
|
||||||
|
intermediate = gs_texrender_get_texture(rt);
|
||||||
|
if (!intermediate) {
|
||||||
|
P_LOG_ERROR("<filter-blur:%s> Failed to get intermediate texture.",
|
||||||
|
name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
blurred = intermediate;
|
||||||
|
}
|
||||||
if (blurred == nullptr) {
|
if (blurred == nullptr) {
|
||||||
obs_source_skip_video_filter(m_source);
|
obs_source_skip_video_filter(m_source);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#pragma endregion Blur
|
||||||
|
|
||||||
|
#pragma region YUV -> RGB or straight draw
|
||||||
// Draw final effect
|
// Draw final effect
|
||||||
{
|
{
|
||||||
gs_effect_t* finalEffect = defaultEffect;
|
gs_effect_t* finalEffect = defaultEffect;
|
||||||
|
@ -543,9 +598,19 @@ void Filter::Blur::Instance::video_render(gs_effect_t *effect) {
|
||||||
technique = "YUVToRGB";
|
technique = "YUVToRGB";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set up camera stuff
|
||||||
|
gs_set_cull_mode(GS_NEITHER);
|
||||||
|
gs_reset_blend_state();
|
||||||
|
gs_enable_blending(true);
|
||||||
|
gs_blend_function(GS_BLEND_SRCALPHA, GS_BLEND_INVSRCALPHA);
|
||||||
|
gs_enable_depth_test(false);
|
||||||
|
gs_enable_stencil_test(false);
|
||||||
|
gs_enable_stencil_write(false);
|
||||||
|
gs_enable_color(true, true, true, true);
|
||||||
|
|
||||||
gs_eparam_t* param = gs_effect_get_param_by_name(finalEffect, "image");
|
gs_eparam_t* param = gs_effect_get_param_by_name(finalEffect, "image");
|
||||||
if (!param) {
|
if (!param) {
|
||||||
PLOG_ERROR("<filter-blur:Final> Failed to set image param.");
|
P_LOG_ERROR("<filter-blur:Final> Failed to set image param.");
|
||||||
failed = true;
|
failed = true;
|
||||||
} else {
|
} else {
|
||||||
gs_effect_set_texture(param, blurred);
|
gs_effect_set_texture(param, blurred);
|
||||||
|
@ -554,6 +619,7 @@ void Filter::Blur::Instance::video_render(gs_effect_t *effect) {
|
||||||
gs_draw_sprite(blurred, 0, baseW, baseH);
|
gs_draw_sprite(blurred, 0, baseW, baseH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#pragma endregion YUV -> RGB or straight draw
|
||||||
|
|
||||||
if (failed) {
|
if (failed) {
|
||||||
obs_source_skip_video_filter(m_source);
|
obs_source_skip_video_filter(m_source);
|
||||||
|
@ -561,175 +627,148 @@ void Filter::Blur::Instance::video_render(gs_effect_t *effect) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gs_texture_t* Filter::Blur::Instance::blur_render(gs_texture_t* input, uint32_t baseW, uint32_t baseH) {
|
gs_effect_param* gs_effect_get_param(gs_effect_t* effect, const char* name) {
|
||||||
bool failed = false;
|
gs_effect_param* p = gs_effect_get_param_by_name(effect, name);
|
||||||
gs_texture_t *intermediate;
|
if (!p)
|
||||||
|
P_LOG_ERROR("<filter-blur> Failed to find parameter %s in effect.", name);
|
||||||
#pragma region Horizontal Pass
|
return p;
|
||||||
gs_texrender_reset(m_rtHorizontal);
|
|
||||||
if (!gs_texrender_begin(m_rtHorizontal, baseW, baseH)) {
|
|
||||||
PLOG_ERROR("<filter-blur:Horizontal> Failed to initialize.");
|
|
||||||
return nullptr;
|
|
||||||
} else {
|
|
||||||
gs_ortho(0, (float)baseW, 0, (float)baseH, -1, 1);
|
|
||||||
|
|
||||||
// Clear to Black
|
|
||||||
vec4 black;
|
|
||||||
vec4_zero(&black);
|
|
||||||
gs_clear(GS_CLEAR_COLOR | GS_CLEAR_DEPTH, &black, 0, 0);
|
|
||||||
|
|
||||||
// Set up camera stuff
|
|
||||||
gs_set_cull_mode(GS_NEITHER);
|
|
||||||
gs_reset_blend_state();
|
|
||||||
gs_blend_function_separate(
|
|
||||||
gs_blend_type::GS_BLEND_ONE,
|
|
||||||
gs_blend_type::GS_BLEND_ZERO,
|
|
||||||
gs_blend_type::GS_BLEND_ONE,
|
|
||||||
gs_blend_type::GS_BLEND_ZERO);
|
|
||||||
gs_enable_depth_test(false);
|
|
||||||
gs_enable_stencil_test(false);
|
|
||||||
gs_enable_stencil_write(false);
|
|
||||||
gs_enable_color(true, true, true, true);
|
|
||||||
|
|
||||||
// Prepare Effect
|
|
||||||
if (!apply_effect_param(input, (float)(1.0 / baseW), 0)) {
|
|
||||||
PLOG_ERROR("<filter-blur:Horizontal> Failed to set effect parameters.");
|
|
||||||
failed = true;
|
|
||||||
} else {
|
|
||||||
while (gs_effect_loop(m_effect, "Draw")) {
|
|
||||||
gs_draw_sprite(input, 0, baseW, baseH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gs_texrender_end(m_rtHorizontal);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (failed) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
intermediate = gs_texrender_get_texture(m_rtHorizontal);
|
|
||||||
if (!intermediate) {
|
|
||||||
PLOG_ERROR("<filter-blur:Horizontal> Failed to get intermediate texture.");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
#pragma endregion Horizontal Pass
|
|
||||||
|
|
||||||
#pragma region Vertical Pass
|
|
||||||
gs_texrender_reset(m_rtVertical);
|
|
||||||
if (!gs_texrender_begin(m_rtVertical, baseW, baseH)) {
|
|
||||||
PLOG_ERROR("<filter-blur:Vertical> Failed to initialize.");
|
|
||||||
return nullptr;
|
|
||||||
} else {
|
|
||||||
gs_ortho(0, (float)baseW, 0, (float)baseH, -1, 1);
|
|
||||||
|
|
||||||
// Clear to Black
|
|
||||||
vec4 black;
|
|
||||||
vec4_zero(&black);
|
|
||||||
gs_clear(GS_CLEAR_COLOR | GS_CLEAR_DEPTH, &black, 0, 0);
|
|
||||||
|
|
||||||
// Set up camera stuff
|
|
||||||
gs_set_cull_mode(GS_NEITHER);
|
|
||||||
gs_reset_blend_state();
|
|
||||||
gs_blend_function_separate(
|
|
||||||
gs_blend_type::GS_BLEND_ONE,
|
|
||||||
gs_blend_type::GS_BLEND_ZERO,
|
|
||||||
gs_blend_type::GS_BLEND_ONE,
|
|
||||||
gs_blend_type::GS_BLEND_ZERO);
|
|
||||||
gs_enable_depth_test(false);
|
|
||||||
gs_enable_stencil_test(false);
|
|
||||||
gs_enable_stencil_write(false);
|
|
||||||
gs_enable_color(true, true, true, true);
|
|
||||||
|
|
||||||
// Prepare Effect
|
|
||||||
if (!apply_effect_param(intermediate, 0, (float)(1.0 / baseH))) {
|
|
||||||
PLOG_ERROR("<filter-blur:Vertical> Failed to set effect parameters.");
|
|
||||||
failed = true;
|
|
||||||
} else {
|
|
||||||
while (gs_effect_loop(m_effect, "Draw")) {
|
|
||||||
gs_draw_sprite(intermediate, 0, baseW, baseH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gs_texrender_end(m_rtVertical);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (failed) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
intermediate = gs_texrender_get_texture(m_rtVertical);
|
|
||||||
if (!intermediate) {
|
|
||||||
PLOG_ERROR("<filter-blur:Vertical> Failed to get intermediate texture.");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
#pragma endregion Vertical Pass
|
|
||||||
|
|
||||||
return intermediate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Filter::Blur::Instance::apply_effect_param(gs_texture_t* texture, float uvTexelX, float uvTexelY) {
|
bool gs_set_param_int(gs_effect_t* effect, const char* name, int value) {
|
||||||
|
gs_effect_param* p = nullptr;
|
||||||
|
if (nullptr != (p = gs_effect_get_param(effect, name))) {
|
||||||
|
gs_effect_set_int(p, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
P_LOG_ERROR("<filter-blur> Failed to set value %d for parameter %s in"
|
||||||
|
" effect.", value, name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gs_set_param_float(gs_effect_t* effect, const char* name, float value) {
|
||||||
|
gs_effect_param* p = nullptr;
|
||||||
|
if (nullptr != (p = gs_effect_get_param(effect, name))) {
|
||||||
|
gs_effect_set_float(p, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
P_LOG_ERROR("<filter-blur> Failed to set value %f for parameter %s in"
|
||||||
|
" effect.", value, name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gs_set_param_float2(gs_effect_t* effect, const char* name, vec2* value) {
|
||||||
|
gs_effect_param* p = nullptr;
|
||||||
|
if (nullptr != (p = gs_effect_get_param(effect, name))) {
|
||||||
|
gs_effect_set_vec2(p, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
P_LOG_ERROR("<filter-blur> Failed to set value {%f,%f} for parameter %s"
|
||||||
|
" in effect.", value->x, value->y, name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gs_set_param_float3(gs_effect_t* effect, const char* name, vec3* value) {
|
||||||
|
gs_effect_param* p = nullptr;
|
||||||
|
if (nullptr != (p = gs_effect_get_param(effect, name))) {
|
||||||
|
gs_effect_set_vec3(p, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
P_LOG_ERROR("<filter-blur> Failed to set value {%f,%f,%f} for parameter"
|
||||||
|
"%s in effect.", value->x, value->y, value->z, name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gs_set_param_float4(gs_effect_t* effect, const char* name, vec4* value) {
|
||||||
|
gs_effect_param* p = nullptr;
|
||||||
|
if (nullptr != (p = gs_effect_get_param(effect, name))) {
|
||||||
|
gs_effect_set_vec4(p, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
P_LOG_ERROR("<filter-blur> Failed to set value {%f,%f,%f,%f} for"
|
||||||
|
" parameter %s in effect.", value->x, value->y, value->z,
|
||||||
|
value->w, name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gs_set_param_texture(gs_effect_t* effect, const char* name, gs_texture_t* value) {
|
||||||
|
gs_effect_param* p = nullptr;
|
||||||
|
if (nullptr != (p = gs_effect_get_param(effect, name))) {
|
||||||
|
gs_effect_set_texture(p, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
P_LOG_ERROR("<filter-blur> Failed to set texture for"
|
||||||
|
" parameter %s in effect.", name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Filter::Blur::Instance::apply_shared_param(gs_texture_t* input, float texelX, float texelY) {
|
||||||
|
bool result = true;
|
||||||
|
|
||||||
|
result = result && gs_set_param_texture(m_effect, "u_image", input);
|
||||||
|
|
||||||
|
vec2 imageSize;
|
||||||
|
vec2_set(&imageSize,
|
||||||
|
(float)gs_texture_get_width(input),
|
||||||
|
(float)gs_texture_get_height(input));
|
||||||
|
result = result && gs_set_param_float2(m_effect, "u_imageSize", &imageSize);
|
||||||
|
|
||||||
|
vec2 imageTexelDelta;
|
||||||
|
vec2_set(&imageTexelDelta, 1.0f, 1.0f);
|
||||||
|
vec2_div(&imageTexelDelta, &imageTexelDelta, &imageSize);
|
||||||
|
result = result && gs_set_param_float2(m_effect, "u_imageTexelDelta", &imageTexelDelta);
|
||||||
|
|
||||||
|
vec2 texel; vec2_set(&texel, texelX, texelY);
|
||||||
|
result = result && gs_set_param_float2(m_effect, "u_texelDelta", &texel);
|
||||||
|
|
||||||
|
result = result && gs_set_param_int(m_effect, "u_radius", (int)m_size);
|
||||||
|
result = result && gs_set_param_int(m_effect, "u_diameter", (int)(1 + (m_size * 2)));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Filter::Blur::Instance::apply_bilateral_param() {
|
||||||
gs_eparam_t *param;
|
gs_eparam_t *param;
|
||||||
|
|
||||||
// UV Stepping
|
if (m_type != Type::Bilateral)
|
||||||
param = gs_effect_get_param_by_name(m_effect, "texel");
|
|
||||||
if (!param) {
|
|
||||||
PLOG_ERROR("<filter-blur> Failed to set texel param.");
|
|
||||||
return false;
|
return false;
|
||||||
} else {
|
|
||||||
PLOG_DEBUG("<filter-blur> Applying texel parameter.");
|
|
||||||
vec2 texel;
|
|
||||||
vec2_set(&texel, uvTexelX, uvTexelY);
|
|
||||||
gs_effect_set_vec2(param, &texel);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter Width
|
|
||||||
param = gs_effect_get_param_by_name(m_effect, "widthHalf");
|
|
||||||
if (!param) {
|
|
||||||
PLOG_ERROR("<filter-blur> Failed to set widthHalf param.");
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
PLOG_DEBUG("<filter-blur> Applying widthHalf parameter.");
|
|
||||||
gs_effect_set_int(param, (int)m_size);
|
|
||||||
}
|
|
||||||
param = gs_effect_get_param_by_name(m_effect, "width");
|
|
||||||
if (!param) {
|
|
||||||
PLOG_ERROR("<filter-blur> Failed to set width param.");
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
PLOG_DEBUG("<filter-blur> Applying width parameter.");
|
|
||||||
gs_effect_set_int(param, (int)(1 + m_size * 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Texture
|
|
||||||
param = gs_effect_get_param_by_name(m_effect, "image");
|
|
||||||
if (!param) {
|
|
||||||
PLOG_ERROR("<filter-blur> Failed to set image param.");
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
PLOG_DEBUG("<filter-blur> Applying image parameter.");
|
|
||||||
gs_effect_set_texture(param, texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bilateral Blur
|
// Bilateral Blur
|
||||||
if (m_type == Type::Bilateral) {
|
param = gs_effect_get_param_by_name(m_effect, "bilateralSmoothing");
|
||||||
param = gs_effect_get_param_by_name(m_effect, "bilateralSmoothing");
|
if (!param) {
|
||||||
if (!param) {
|
P_LOG_ERROR("<filter-blur> Failed to set bilateralSmoothing param.");
|
||||||
PLOG_ERROR("<filter-blur> Failed to set bilateralSmoothing param.");
|
return false;
|
||||||
return false;
|
} else {
|
||||||
} else {
|
P_LOG_DEBUG("<filter-blur> Applying bilateralSmoothing parameter.");
|
||||||
PLOG_DEBUG("<filter-blur> Applying bilateralSmoothing parameter.");
|
gs_effect_set_float(param,
|
||||||
gs_effect_set_float(param,
|
(float)(m_bilateralSmoothing * (1 + m_size * 2)));
|
||||||
(float)(m_bilateralSmoothing * (1 + m_size * 2)));
|
}
|
||||||
}
|
|
||||||
|
|
||||||
param = gs_effect_get_param_by_name(m_effect, "bilateralSharpness");
|
param = gs_effect_get_param_by_name(m_effect, "bilateralSharpness");
|
||||||
if (!param) {
|
if (!param) {
|
||||||
PLOG_ERROR("<filter-blur> Failed to set bilateralSmoothing param.");
|
P_LOG_ERROR("<filter-blur> Failed to set bilateralSmoothing param.");
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
PLOG_DEBUG("<filter-blur> Applying bilateralSharpness parameter.");
|
P_LOG_DEBUG("<filter-blur> Applying bilateralSharpness parameter.");
|
||||||
gs_effect_set_float(param, (float)(1.0 - m_bilateralSharpness));
|
gs_effect_set_float(param, (float)(1.0 - m_bilateralSharpness));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Filter::Blur::Instance::apply_gaussian_param() {
|
||||||
|
bool result = true;
|
||||||
|
|
||||||
|
if (m_type != Type::Gaussian)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (g_gaussianBlur.kernels.size() >= m_size) {
|
||||||
|
result = result && gs_set_param_texture(m_effect, "kernel",
|
||||||
|
g_gaussianBlur.kernels[m_size - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 kerneltexel;
|
||||||
|
vec2_set(&kerneltexel, 1.0f / (m_size + 1), 0);
|
||||||
|
result = result && gs_set_param_float2(m_effect, "kernelTexel", &kerneltexel);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
@ -21,20 +21,19 @@
|
||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
#include "gs-helper.h"
|
#include "gs-helper.h"
|
||||||
|
|
||||||
#define P_FILTER_BLUR "Filter.Blur"
|
#define S_FILTER_BLUR "Filter.Blur"
|
||||||
#define P_FILTER_BLUR_TYPE "Filter.Blur.Type"
|
#define S_FILTER_BLUR_TYPE "Filter.Blur.Type"
|
||||||
#define P_FILTER_BLUR_TYPE_BOX "Filter.Blur.Type.Box"
|
#define S_FILTER_BLUR_TYPE_BOX "Filter.Blur.Type.Box"
|
||||||
#define P_FILTER_BLUR_TYPE_GAUSSIAN "Filter.Blur.Type.Gaussian"
|
#define S_FILTER_BLUR_TYPE_GAUSSIAN "Filter.Blur.Type.Gaussian"
|
||||||
#define P_FILTER_BLUR_TYPE_BILATERAL "Filter.Blur.Type.Bilateral"
|
#define S_FILTER_BLUR_TYPE_BILATERAL "Filter.Blur.Type.Bilateral"
|
||||||
#define P_FILTER_BLUR_SIZE "Filter.Blur.Size"
|
#define S_FILTER_BLUR_SIZE "Filter.Blur.Size"
|
||||||
|
|
||||||
// Bilateral Blur
|
// Bilateral Blur
|
||||||
#define P_FILTER_BLUR_BILATERAL_SMOOTHING "Filter.Blur.Bilateral.Smoothing"
|
#define S_FILTER_BLUR_BILATERAL_SMOOTHING "Filter.Blur.Bilateral.Smoothing"
|
||||||
#define P_FILTER_BLUR_BILATERAL_SHARPNESS "Filter.Blur.Bilateral.Sharpness"
|
#define S_FILTER_BLUR_BILATERAL_SHARPNESS "Filter.Blur.Bilateral.Sharpness"
|
||||||
|
|
||||||
// Advanced
|
// Advanced
|
||||||
#define P_FILTER_BLUR_ADVANCED "Filter.Blur.Advanced"
|
#define S_FILTER_BLUR_COLORFORMAT "Filter.Blur.ColorFormat"
|
||||||
#define P_FILTER_BLUR_ADVANCED_COLORFORMAT "Filter.Blur.Avanced.ColorFormat"
|
|
||||||
|
|
||||||
namespace Filter {
|
namespace Filter {
|
||||||
class Blur {
|
class Blur {
|
||||||
|
@ -45,7 +44,8 @@ namespace Filter {
|
||||||
static const char *get_name(void *);
|
static const char *get_name(void *);
|
||||||
static void get_defaults(obs_data_t *);
|
static void get_defaults(obs_data_t *);
|
||||||
static obs_properties_t *get_properties(void *);
|
static obs_properties_t *get_properties(void *);
|
||||||
static bool modified_properties(obs_properties_t *, obs_property_t *, obs_data_t *);
|
static bool modified_properties(obs_properties_t *,
|
||||||
|
obs_property_t *, obs_data_t *);
|
||||||
|
|
||||||
static void *create(obs_data_t *, obs_source_t *);
|
static void *create(obs_data_t *, obs_source_t *);
|
||||||
static void destroy(void *);
|
static void destroy(void *);
|
||||||
|
@ -83,10 +83,10 @@ namespace Filter {
|
||||||
void hide();
|
void hide();
|
||||||
void video_tick(float);
|
void video_tick(float);
|
||||||
void video_render(gs_effect_t*);
|
void video_render(gs_effect_t*);
|
||||||
gs_texture_t* blur_render(gs_texture_t* input, uint32_t baseW, uint32_t baseH);
|
bool apply_shared_param(gs_texture_t* input,
|
||||||
|
float texelX, float texelY);
|
||||||
bool apply_effect_param(gs_texture_t* texture,
|
bool apply_bilateral_param();
|
||||||
float uvTexelX, float uvTexelY);
|
bool apply_gaussian_param();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
obs_source_t *m_source;
|
obs_source_t *m_source;
|
||||||
|
|
Loading…
Reference in a new issue