Add blur effect

This commit is contained in:
Nikita Lisitsa 2020-12-09 23:19:27 +03:00
parent 4915b171c7
commit 15fcfdd053
2 changed files with 201 additions and 0 deletions

View file

@ -0,0 +1,33 @@
#pragma once
#include <psemek/gfx/texture.hpp>
#include <psemek/gfx/render_target.hpp>
#include <psemek/util/pimpl.hpp>
namespace psemek::gfx
{
struct hblur
{
hblur(int size, float sigma);
~hblur();
void invoke(texture_2d const & src, render_target const & dst);
private:
psemek_declare_pimpl
};
struct vblur
{
vblur(int size, float sigma);
~vblur();
void invoke(texture_2d const & src, render_target const & dst);
private:
psemek_declare_pimpl
};
}

View file

@ -0,0 +1,168 @@
#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>
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;
in vec2 texcoord;
out vec4 out_color;
void main()
{
vec4 result = vec4(0.0);
for (int i = -BLUR_SIZE; i <= BLUR_SIZE; ++i)
{
result += texture(u_texture, texcoord + float(i) * direction * u_inv_texture_size) * coeffs[i + BLUR_SIZE];
}
out_color = result;
}
)";
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, bool horizontal)
: program(blur_vs, generate_blur_fs_source(size, sigma, horizontal))
{
program.bind();
program["u_texture"] = 0;
}
void invoke(texture_2d const & src, render_target const & dst)
{
gl::Disable(gl::DEPTH_TEST);
gl::Disable(gl::CULL_FACE);
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)
: pimpl_{std::make_unique<struct impl>(size, sigma, 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)
: pimpl_{std::make_unique<struct impl>(size, sigma, false)}
{}
vblur::~vblur() = default;
void vblur::invoke(texture_2d const & src, render_target const & dst)
{
impl().invoke(src, dst);
}
}