178 lines
3.8 KiB
C++
178 lines
3.8 KiB
C++
#include <psemek/gfx/effect/blur.hpp>
|
|
#include <psemek/gfx/program.hpp>
|
|
#include <psemek/gfx/array.hpp>
|
|
|
|
#include <psemek/util/to_string.hpp>
|
|
|
|
#include <iomanip>
|
|
#include <sstream>
|
|
#include <vector>
|
|
|
|
namespace psemek::gfx
|
|
{
|
|
|
|
static char const blur_vs[] =
|
|
R"(#version 330
|
|
|
|
const vec4 vertices[6] = vec4[6](
|
|
vec4(-1.0, -1.0, 0.0, 1.0),
|
|
vec4( 1.0, -1.0, 0.0, 1.0),
|
|
vec4( 1.0, 1.0, 0.0, 1.0),
|
|
|
|
vec4(-1.0, -1.0, 0.0, 1.0),
|
|
vec4( 1.0, 1.0, 0.0, 1.0),
|
|
vec4(-1.0, 1.0, 0.0, 1.0)
|
|
);
|
|
|
|
out vec2 texcoord;
|
|
|
|
void main()
|
|
{
|
|
gl_Position = vertices[gl_VertexID];
|
|
texcoord = vertices[gl_VertexID].xy * 0.5 + vec2(0.5);
|
|
}
|
|
)";
|
|
|
|
static char const blur_fs_template[] =
|
|
R"(#version 330
|
|
|
|
#define BLUR_SIZE @BLUR_SIZE@
|
|
#define BLUR_COEFFS_COUNT 2 * BLUR_SIZE + 1
|
|
|
|
const float coeffs[BLUR_COEFFS_COUNT] = float[BLUR_COEFFS_COUNT](@BLUR_COEFFS@);
|
|
|
|
const vec2 direction = vec2(@BLUR_DIRECTION@);
|
|
|
|
uniform sampler2D u_texture;
|
|
uniform vec2 u_inv_texture_size;
|
|
uniform float u_gamma;
|
|
|
|
in vec2 texcoord;
|
|
|
|
out vec4 out_color;
|
|
|
|
void main()
|
|
{
|
|
vec4 result = vec4(0.0);
|
|
for (int i = -BLUR_SIZE; i <= BLUR_SIZE; ++i)
|
|
{
|
|
vec4 p = texture(u_texture, texcoord + float(i) * direction * u_inv_texture_size);
|
|
float c = coeffs[i + BLUR_SIZE];
|
|
result.rgb += pow(p.rgb, vec3(1.0 / u_gamma)) * p.a * c;
|
|
result.a += p.a * c;
|
|
}
|
|
if (result.a == 0.0)
|
|
out_color = result;
|
|
else
|
|
out_color = vec4(pow(result.rgb / result.a, vec3(u_gamma)), result.a);
|
|
}
|
|
)";
|
|
|
|
static void replace_all(std::string & str, std::string_view old_str, std::string_view new_str)
|
|
{
|
|
for (size_t i = 0;;)
|
|
{
|
|
i = str.find(old_str, i);
|
|
if (i == std::string::npos) break;
|
|
str.replace(i, old_str.size(), new_str);
|
|
i += new_str.size();
|
|
}
|
|
}
|
|
|
|
static std::string generate_blur_fs_source(int size, float sigma, bool horizontal)
|
|
{
|
|
std::string size_str = util::to_string(size);
|
|
|
|
std::string direction_str = horizontal ? "1.0, 0.0" : "0.0, 1.0";
|
|
|
|
std::vector<float> coeffs(2 * size + 1);
|
|
{
|
|
float sum = 0.f;
|
|
for (int i = -size; i <= size; ++i)
|
|
{
|
|
float x = (i / sigma);
|
|
coeffs[i + size] = std::exp(- x * x);
|
|
sum += coeffs[i + size];
|
|
}
|
|
for (auto & c : coeffs)
|
|
c /= sum;
|
|
}
|
|
|
|
std::ostringstream coeffs_ss;
|
|
for (std::size_t i = 0; i < coeffs.size(); ++i)
|
|
{
|
|
if (i > 0) coeffs_ss << ", ";
|
|
coeffs_ss << std::fixed << std::setprecision(10) << coeffs[i];
|
|
}
|
|
|
|
std::string result = blur_fs_template;
|
|
replace_all(result, "@BLUR_SIZE@", size_str);
|
|
replace_all(result, "@BLUR_COEFFS@", coeffs_ss.str());
|
|
replace_all(result, "@BLUR_DIRECTION@", direction_str);
|
|
return result;
|
|
}
|
|
|
|
struct blur_impl
|
|
{
|
|
gfx::program program;
|
|
gfx::array array;
|
|
|
|
blur_impl(int size, float sigma, float gamma, bool horizontal)
|
|
: program(blur_vs, generate_blur_fs_source(size, sigma, horizontal))
|
|
{
|
|
program.bind();
|
|
program["u_texture"] = 0;
|
|
program["u_gamma"] = gamma;
|
|
}
|
|
|
|
void invoke(texture_2d const & src, render_target const & dst)
|
|
{
|
|
gl::Disable(gl::DEPTH_TEST);
|
|
gl::Disable(gl::CULL_FACE);
|
|
gl::Disable(gl::BLEND);
|
|
|
|
dst.bind();
|
|
gl::ActiveTexture(gl::TEXTURE0);
|
|
src.bind();
|
|
array.bind();
|
|
program.bind();
|
|
program["u_inv_texture_size"] = geom::vector{1.f / src.width(), 1.f / src.height()};
|
|
gl::DrawArrays(gl::TRIANGLES, 0, 6);
|
|
}
|
|
};
|
|
|
|
struct hblur::impl
|
|
: blur_impl
|
|
{
|
|
using blur_impl::blur_impl;
|
|
};
|
|
|
|
hblur::hblur(int size, float sigma, float gamma)
|
|
: pimpl_{make_impl(size, sigma, gamma, true)}
|
|
{}
|
|
|
|
hblur::~hblur() = default;
|
|
|
|
void hblur::invoke(texture_2d const & src, render_target const & dst)
|
|
{
|
|
impl().invoke(src, dst);
|
|
}
|
|
|
|
struct vblur::impl
|
|
: blur_impl
|
|
{
|
|
using blur_impl::blur_impl;
|
|
};
|
|
|
|
vblur::vblur(int size, float sigma, float gamma)
|
|
: pimpl_{make_impl(size, sigma, gamma, false)}
|
|
{}
|
|
|
|
vblur::~vblur() = default;
|
|
|
|
void vblur::invoke(texture_2d const & src, render_target const & dst)
|
|
{
|
|
impl().invoke(src, dst);
|
|
}
|
|
|
|
}
|