Original implementation of Blur Image Filters

With Gaussian, fast Gaussian, Hashed and morphological (opening and closing) methods. Remembering shader code for other fast methods.
This commit is contained in:
Bruno Herbelin
2022-05-31 00:34:37 +02:00
parent 662d8bcfda
commit e3bb95b3dd
27 changed files with 628 additions and 155 deletions

View File

@@ -567,6 +567,9 @@ set(VMIX_RSC_FILES
./rsc/shaders/filters/blur_1.glsl
./rsc/shaders/filters/blur_2.glsl
./rsc/shaders/filters/hashedblur.glsl
./rsc/shaders/filters/circularblur.glsl
./rsc/shaders/filters/hashederosion.glsl
./rsc/shaders/filters/hasheddilation.glsl
./rsc/shaders/filters/sharp.glsl
./rsc/shaders/filters/sharpen.glsl
./rsc/shaders/filters/sharpen_1.glsl

View File

@@ -147,6 +147,9 @@ void CloneSource::setFilter(FrameBufferFilter::Type T)
case FrameBufferFilter::FILTER_DELAY:
filter_ = new DelayFilter;
break;
case FrameBufferFilter::FILTER_BLUR:
filter_ = new BlurFilter;
break;
case FrameBufferFilter::FILTER_IMAGE:
filter_ = new ImageFilter;
break;
@@ -157,7 +160,7 @@ void CloneSource::setFilter(FrameBufferFilter::Type T)
}
// TODO : resampling of renderbuffer
// TODO : resampling of renderbuffer ?
}

View File

@@ -81,8 +81,6 @@ FrameBuffer::FrameBuffer(glm::vec3 resolution, FrameBufferFlags flags): flags_(f
attrib_.viewport = glm::ivec2(resolution);
setProjectionArea(glm::vec2(1.f, 1.f));
attrib_.clear_color = glm::vec4(0.f, 0.f, 0.f, 0.f);
for(int i=0; i<MIPMAP_LEVEL; ++i)
mipmap_framebufferid_[i] = 0;
}
FrameBuffer::FrameBuffer(uint width, uint height, FrameBufferFlags flags): flags_(flags),
@@ -91,8 +89,6 @@ FrameBuffer::FrameBuffer(uint width, uint height, FrameBufferFlags flags): flags
attrib_.viewport = glm::ivec2(width, height);
setProjectionArea(glm::vec2(1.f, 1.f));
attrib_.clear_color = glm::vec4(0.f, 0.f, 0.f, 0.f);
for(int i=0; i<MIPMAP_LEVEL; ++i)
mipmap_framebufferid_[i] = 0;
}
void FrameBuffer::init()
@@ -107,24 +103,16 @@ void FrameBuffer::init()
if (flags_ & FrameBuffer_mipmap) {
glTexStorage2D(GL_TEXTURE_2D, MIPMAP_LEVEL + 1, (flags_ & FrameBuffer_alpha) ? GL_RGBA8 : GL_RGB8, attrib_.viewport.x, attrib_.viewport.y);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
// calculate GPU memory usage
int width = attrib_.viewport.x;
int height = attrib_.viewport.y;
for(int i=0; i < MIPMAP_LEVEL; ++i) {
mem_usage_ += ( width * height * (flags_ & FrameBuffer_alpha?4:3) ) / 1024;
width = MAX(1, (width / 2));
height = MAX(1, (height / 2));
}
}
// default : create simple texture for RGB(A)
else {
glTexStorage2D(GL_TEXTURE_2D, 1, (flags_ & FrameBuffer_alpha) ? GL_RGBA8 : GL_RGB8, attrib_.viewport.x, attrib_.viewport.y);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// calculate GPU memory usage
mem_usage_ += ( attrib_.viewport.x * attrib_.viewport.y * (flags_ & FrameBuffer_alpha?4:3) ) / 1024;
}
// calculate GPU memory usage (for debug only)
mem_usage_ += ( attrib_.viewport.x * attrib_.viewport.y * (flags_ & FrameBuffer_alpha?4:3) ) / 1024;
// common texture parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
@@ -179,12 +167,14 @@ void FrameBuffer::init()
// attach multiple FBOs to the mipmaped texture
if (flags_ & FrameBuffer_mipmap) {
glGenFramebuffers(MIPMAP_LEVEL, mipmap_framebufferid_);
for(int i=0; i < MIPMAP_LEVEL; ++i) {
glBindFramebuffer(GL_FRAMEBUFFER, mipmap_framebufferid_[i]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureid_, i + 1);
// calculate GPU memory usage
int width = attrib_.viewport.x;
int height = attrib_.viewport.y;
for(int i=1; i < MIPMAP_LEVEL; ++i) {
width = MAX(1, (width / 2));
height = MAX(1, (height / 2));
mem_usage_ += ( width * height * (flags_ & FrameBuffer_alpha?4:3) ) / 1024;
}
#ifdef FRAMEBUFFER_DEBUG
g_printerr("mipmap (%d) - ", MIPMAP_LEVEL);
#endif
@@ -208,8 +198,6 @@ FrameBuffer::~FrameBuffer()
glDeleteFramebuffers(1, &framebufferid_);
if (multisampling_framebufferid_)
glDeleteFramebuffers(1, &multisampling_framebufferid_);
if (mipmap_framebufferid_[0])
glDeleteFramebuffers(MIPMAP_LEVEL, mipmap_framebufferid_);
if (textureid_)
glDeleteTextures(1, &textureid_);
if (multisampling_textureid_)
@@ -260,11 +248,6 @@ void FrameBuffer::resize(int width, int height)
glDeleteFramebuffers(1, &multisampling_framebufferid_);
multisampling_framebufferid_ = 0;
if (mipmap_framebufferid_[0])
glDeleteFramebuffers(MIPMAP_LEVEL, mipmap_framebufferid_);
for(int i=0; i<MIPMAP_LEVEL; ++i)
mipmap_framebufferid_[i] = 0;
if (textureid_)
glDeleteTextures(1, &textureid_);
textureid_ = 0;
@@ -305,28 +288,14 @@ void FrameBuffer::end()
0, 0, attrib_.viewport.x, attrib_.viewport.y, GL_COLOR_BUFFER_BIT, GL_NEAREST);
}
// if mipmapped texture, fill FBOs
if (flags_ & FrameBuffer_mipmap) {
// First read full-size framebuffer
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebufferid_);
int width = attrib_.viewport.x;
int height = attrib_.viewport.y;
// iterate over all levels of mipmaps
for(int i=0; i < MIPMAP_LEVEL; ++i) {
// draw on Mipmap level i
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mipmap_framebufferid_[i]);
// blit into half-sized mipmap i (with linear nearest color)
glBlitFramebuffer(0, 0, width, height,
0, 0, width / 2, height / 2, GL_COLOR_BUFFER_BIT, GL_NEAREST);
// next iteration (i+1), read half-size mipmap level i
glBindFramebuffer(GL_READ_FRAMEBUFFER, mipmap_framebufferid_[i]);
width = MAX(1, (width / 2));
height = MAX(1, (height / 2));
}
}
FrameBuffer::release();
if (flags_ & FrameBuffer_mipmap) {
glBindTexture(GL_TEXTURE_2D, textureid_);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
}
Rendering::manager().popAttrib();
}

View File

@@ -4,7 +4,7 @@
#include "RenderingManager.h"
#define FBI_JPEG_QUALITY 90
#define MIPMAP_LEVEL 6
#define MIPMAP_LEVEL 7
/**
* @brief The FrameBufferImage class stores an RGB image in RAM
@@ -109,7 +109,7 @@ private:
glm::mat4 projection_;
glm::vec2 projection_area_;
uint textureid_, multisampling_textureid_;
uint framebufferid_, multisampling_framebufferid_, mipmap_framebufferid_[MIPMAP_LEVEL] = {};
uint framebufferid_, multisampling_framebufferid_;
uint mem_usage_;
};

View File

@@ -6,7 +6,7 @@
#include "FrameBufferFilter.h"
const char* FrameBufferFilter::type_label[FrameBufferFilter::FILTER_INVALID] = {
"None", "Delay", "Shader code"
"None", "Delay", "Blur", "Shader code"
};
FrameBufferFilter::FrameBufferFilter() : enabled_(true), input_(nullptr)

View File

@@ -20,6 +20,7 @@ public:
typedef enum {
FILTER_PASSTHROUGH = 0,
FILTER_DELAY,
FILTER_BLUR,
FILTER_IMAGE,
FILTER_INVALID
} Type;

View File

@@ -772,6 +772,52 @@ void ImGuiVisitor::visit (DelayFilter& f)
}
}
void ImGuiVisitor::visit (BlurFilter& f)
{
std::ostringstream oss;
oss << "Blur ";
// Method selection
if (ImGuiToolkit::IconButton(7, 16)) {
f.setMethod( 0 );
oss << BlurFilter::method_label[0];
Action::manager().store(oss.str());
}
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
int m = (int) f.method();
if (ImGui::Combo("Method", &m, BlurFilter::method_label, IM_ARRAYSIZE(BlurFilter::method_label) )) {
f.setMethod( m );
oss << BlurFilter::method_label[m];
Action::manager().store(oss.str());
}
// List of parameters
std::map<std::string, float> filter_parameters = f.program().parameters();
for (auto param = filter_parameters.begin(); param != filter_parameters.end(); ++param)
{
ImGui::PushID( param->first.c_str() );
float v = param->second;
if (ImGuiToolkit::IconButton(13, 14)) {
v = 0.f;
f.setProgramParameter(param->first, v);
}
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::SliderFloat( param->first.c_str(), &v, 0.f, 1.f, "%.2f")) {
f.setProgramParameter(param->first, v);
}
if (ImGui::IsItemDeactivatedAfterEdit()) {
oss << BlurFilter::method_label[ f.method() ];
oss << " : " << param->first << " " << std::setprecision(3) <<param->second;
Action::manager().store(oss.str());
}
ImGui::PopID();
}
}
void ImGuiVisitor::visit (ImageFilter& f)
{
// Selection of Algorithm
@@ -781,7 +827,7 @@ void ImGuiVisitor::visit (ImageFilter& f)
}
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::BeginCombo("##Filters", f.program().name().c_str()) )
if (ImGui::BeginCombo("Algorithm", f.program().name().c_str()) )
{
for (auto p = FilteringProgram::presets.begin(); p != FilteringProgram::presets.end(); ++p){
if (ImGui::Selectable( p->name().c_str() )) {
@@ -791,26 +837,21 @@ void ImGuiVisitor::visit (ImageFilter& f)
}
ImGui::EndCombo();
}
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::Text("Algorithm");
// List of parameters
std::map<std::string, float> filter_parameters = f.program().parameters();
FilteringProgram target = f.program();
for (auto param = filter_parameters.begin(); param != filter_parameters.end(); ++param)
{
ImGui::PushID( param->first.c_str() );
float v = param->second;
if (ImGuiToolkit::IconButton(11, 11)) {
if (ImGuiToolkit::IconButton(13, 14)) {
v = 0.f;
target.setParameter(param->first, v);
f.setProgram( target );
f.setProgramParameter(param->first, v);
}
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::SliderFloat( param->first.c_str(), &v, 0.f, 1.f, "%.2f")) {
target.setParameter(param->first, v);
f.setProgram( target );
f.setProgramParameter(param->first, v);
}
if (ImGui::IsItemDeactivatedAfterEdit()) {
// TODO UNDO

View File

@@ -39,6 +39,7 @@ public:
void visit (FrameBufferFilter&) override;
void visit (PassthroughFilter&) override;
void visit (DelayFilter&) override;
void visit (BlurFilter&) override;
void visit (ImageFilter&) override;
};

View File

@@ -128,6 +128,12 @@ std::string FilteringProgram::getFilterCodeDefault()
return filterDefault;
}
////////////////////////////////////////
///// //
//// PROGRAM DEFINING A FILTER ///
/// ////
////////////////////////////////////////
FilteringProgram::FilteringProgram() : name_("None"), code_({"shaders/filters/default.glsl",""}), two_pass_filter_(false)
{
@@ -181,6 +187,12 @@ bool FilteringProgram::operator!= (const FilteringProgram& other) const
}
////////////////////////////////////////
///// //
//// IMAGE SHADER FOR FILTERS ///
/// ////
////////////////////////////////////////
class ImageFilteringShader : public ImageShader
{
// GLSL Program
@@ -309,6 +321,12 @@ void ImageFilteringShader::copy(ImageFilteringShader const& S)
}
////////////////////////////////////////
///// //
//// GENERIC IMAGE FILTER ///
/// ////
////////////////////////////////////////
ImageFilter::ImageFilter (): FrameBufferFilter(), buffers_({nullptr, nullptr})
{
// surface and shader for first pass
@@ -374,7 +392,7 @@ void ImageFilter::draw (FrameBuffer *input)
// (re)create framebuffer for result of first-pass
if (buffers_.first != nullptr)
delete buffers_.first;
// FBO with mipmapping
// FBO
buffers_.first = new FrameBuffer( input_->resolution(), input_->flags() );
// enforce framebuffer if first-pass is created now, filled with input framebuffer
input_->blit( buffers_.first );
@@ -422,25 +440,152 @@ void ImageFilter::setProgram(const FilteringProgram &f, std::promise<std::string
// always keep local copy
program_ = f;
// // force disable when using default filter
// setEnabled( program_ != FilteringProgram::presets.front() );
// change code
std::pair<std::string, std::string> codes = program_.code();
// FIRST PASS
// set code to the shader for first-pass
shaders_.first->setCode( codes.first, ret );
// SECOND PASS
if ( program_.isTwoPass() )
// set the code to the shader for second-pass
shaders_.second->setCode( codes.second );
updateParameters();
}
void ImageFilter::updateParameters()
{
// change uniforms
shaders_.first->uniforms_ = program_.parameters();
// SECOND PASS
if ( program_.isTwoPass() ) {
// set the code to the shader for second-pass
shaders_.second->setCode( codes.second );
// change uniforms
if ( program_.isTwoPass() )
shaders_.second->uniforms_ = program_.parameters();
}
}
void ImageFilter::setProgramParameters(const std::map< std::string, float > &parameters)
{
program_.setParameters(parameters);
updateParameters();
}
void ImageFilter::setProgramParameter(const std::string &p, float value)
{
program_.setParameter(p, value);
updateParameters();
}
////////////////////////////////////////
///// //
//// BLURING FILTERS ///
/// ////
////////////////////////////////////////
const char* BlurFilter::method_label[BlurFilter::BLUR_INVALID] = {
"Gaussian", "Hash", "Openning", "Closing", "Fast 2x2"
};
std::vector< FilteringProgram > BlurFilter::programs_ = {
FilteringProgram("Gaussian", "shaders/filters/blur_1.glsl", "shaders/filters/blur_2.glsl", { { "Radius", 0.5} }),
FilteringProgram("Hashed", "shaders/filters/hashedblur.glsl", "", { { "Iterations", 0.5 }, { "Radius", 0.5} }),
FilteringProgram("Openning", "shaders/filters/hashederosion.glsl", "shaders/filters/hasheddilation.glsl", { { "Radius", 0.5} }),
FilteringProgram("Closing", "shaders/filters/hasheddilation.glsl", "shaders/filters/hashederosion.glsl", { { "Radius", 0.5} }),
FilteringProgram("Fast 2x2", "shaders/filters/blur.glsl", "", { })
};
BlurFilter::BlurFilter (): ImageFilter(), method_(BLUR_INVALID), mipmap_buffer_(nullptr)
{
mipmap_surface_ = new Surface;
}
BlurFilter::~BlurFilter ()
{
delete mipmap_surface_;
if ( mipmap_buffer_!= nullptr )
delete mipmap_buffer_;
}
void BlurFilter::setMethod(int method)
{
method_ = (BlurMethod) CLAMP(method, BLUR_GAUSSIAN, BLUR_INVALID-1);
setProgram( programs_[ (int) method_] );
}
void BlurFilter::draw (FrameBuffer *input)
{
// Default to Gaussian blur
if (method_ == BLUR_INVALID)
setMethod( BLUR_GAUSSIAN );
// if input changed (typically on first draw)
if (input_ != input) {
// keep reference to input framebuffer
input_ = input;
// create zero-pass surface taking as texture the input framebuffer
mipmap_surface_->setTextureIndex( input_->texture() );
// FBO with mipmapping
// (re)create framebuffer for mipmapped input
if ( mipmap_buffer_!= nullptr )
delete mipmap_buffer_;
FrameBuffer::FrameBufferFlags f = input_->flags();
mipmap_buffer_ = new FrameBuffer( input_->resolution(), f | FrameBuffer::FrameBuffer_mipmap );
// enforce framebuffer created now, filled with input framebuffer
input_->blit( mipmap_buffer_ );
// create first-pass surface and shader, taking as texture the input framebuffer
surfaces_.first->setTextureIndex( mipmap_buffer_->texture() );
shaders_.first->mask_texture = input_->texture();
// (re)create framebuffer for result of first-pass
if (buffers_.first != nullptr)
delete buffers_.first;
buffers_.first = new FrameBuffer( input_->resolution(), f | FrameBuffer::FrameBuffer_mipmap );
// enforce framebuffer of first-pass is created now, filled with input framebuffer
mipmap_buffer_->blit( buffers_.first );
// create second-pass surface and shader, taking as texture the first-pass framebuffer
surfaces_.second->setTextureIndex( buffers_.first->texture() );
shaders_.second->mask_texture = input_->texture();
// (re)create framebuffer for result of second-pass
if (buffers_.second != nullptr)
delete buffers_.second;
buffers_.second = new FrameBuffer( input_->resolution(), f );
}
if ( enabled() )
{
// ZERO PASS
// render input surface into frame buffer with Mipmapping (Levels of Details)
mipmap_buffer_->begin();
mipmap_surface_->draw(glm::identity<glm::mat4>(), mipmap_buffer_->projection());
mipmap_buffer_->end();
// FIRST PASS
// render mipmapped texture into frame buffer
buffers_.first->begin();
surfaces_.first->draw(glm::identity<glm::mat4>(), buffers_.first->projection());
buffers_.first->end();
// SECOND PASS
if ( program().isTwoPass() ) {
// render filtered surface from first pass into frame buffer
buffers_.second->begin();
surfaces_.second->draw(glm::identity<glm::mat4>(), buffers_.second->projection());
buffers_.second->end();
}
}
}
void BlurFilter::accept (Visitor& v)
{
FrameBufferFilter::accept(v);
v.visit(*this);
}

View File

@@ -3,6 +3,7 @@
#include <map>
#include <list>
#include <vector>
#include <string>
#include <future>
@@ -69,22 +70,21 @@ class ImageFilter : public FrameBufferFilter
{
FilteringProgram program_;
std::pair< Surface *, Surface *> surfaces_;
std::pair< FrameBuffer *, FrameBuffer * > buffers_;
std::pair< ImageFilteringShader *, ImageFilteringShader *> shaders_;
public:
// instanciate an image filter at given resolution
ImageFilter();
~ImageFilter();
virtual ~ImageFilter();
// set the program
void setProgram(const FilteringProgram &f, std::promise<std::string> *ret = nullptr);
// get copy of the program
FilteringProgram program() const;
// update parameters of program
void setProgramParameters(const std::map< std::string, float > &parameters);
void setProgramParameter(const std::string &p, float value);
// implementation of FrameBufferFilter
Type type() const override { return FrameBufferFilter::FILTER_IMAGE; }
uint texture () const override;
@@ -92,7 +92,51 @@ public:
void update (float dt) override;
void draw (FrameBuffer *input) override;
void accept (Visitor& v) override;
protected:
std::pair< Surface *, Surface *> surfaces_;
std::pair< FrameBuffer *, FrameBuffer * > buffers_;
std::pair< ImageFilteringShader *, ImageFilteringShader *> shaders_;
void updateParameters();
};
class BlurFilter : public ImageFilter
{
public:
BlurFilter();
virtual ~BlurFilter();
// Algorithms used for blur
typedef enum {
BLUR_GAUSSIAN = 0,
BLUR_HASH,
BLUR_OPENNING,
BLUR_CLOSING,
BLUR_FAST,
BLUR_INVALID
} BlurMethod;
static const char* method_label[BLUR_INVALID];
BlurMethod method () const { return method_; }
void setMethod(int method);
// implementation of FrameBufferFilter
Type type() const override { return FrameBufferFilter::FILTER_BLUR; }
void draw (FrameBuffer *input) override;
void accept (Visitor& v) override;
private:
BlurMethod method_;
static std::vector< FilteringProgram > programs_;
Surface *mipmap_surface_;
FrameBuffer *mipmap_buffer_;
};
#endif // IMAGEFILTER_H

View File

@@ -1225,6 +1225,30 @@ void SessionLoader::visit (DelayFilter& f)
f.setDelay(d);
}
void SessionLoader::visit (BlurFilter& f)
{
int m = 0;
xmlCurrent_->QueryIntAttribute("method", &m);
f.setMethod(m);
std::map< std::string, float > filter_params;
XMLElement* parameters = xmlCurrent_->FirstChildElement("parameters");
if (parameters) {
XMLElement* param = parameters->FirstChildElement("uniform");
for( ; param ; param = param->NextSiblingElement())
{
float val = 0.f;
param->QueryFloatAttribute("value", &val);
const char * name;
param->QueryStringAttribute("name", &name);
if (name)
filter_params[name] = val;
}
}
f.setProgramParameters(filter_params);
}
void SessionLoader::visit (ImageFilter& f)
{
const char * filter_name;

View File

@@ -64,6 +64,7 @@ public:
void visit (CloneSource& s) override;
void visit (FrameBufferFilter&) override;
void visit (DelayFilter&) override;
void visit (BlurFilter&) override;
void visit (ImageFilter&) override;
// callbacks

View File

@@ -694,6 +694,22 @@ void SessionVisitor::visit (DelayFilter& f)
xmlCurrent_->SetAttribute("delay", f.delay());
}
void SessionVisitor::visit (BlurFilter& f)
{
xmlCurrent_->SetAttribute("method", (int) f.method());
std::map< std::string, float > filter_params = f.program().parameters();
XMLElement *parameters = xmlDoc_->NewElement( "parameters" );
xmlCurrent_->InsertEndChild(parameters);
for (auto p = filter_params.begin(); p != filter_params.end(); ++p)
{
XMLElement *param = xmlDoc_->NewElement( "uniform" );
param->SetAttribute("name", p->first.c_str() );
param->SetAttribute("value", p->second );
parameters->InsertEndChild(param);
}
}
void SessionVisitor::visit (ImageFilter& f)
{
xmlCurrent_->SetAttribute("name", f.program().name().c_str() );

View File

@@ -72,6 +72,7 @@ public:
void visit (CloneSource& s) override;
void visit (FrameBufferFilter&) override;
void visit (DelayFilter&) override;
void visit (BlurFilter&) override;
void visit (ImageFilter&) override;
// callbacks

View File

@@ -44,6 +44,7 @@ class MultiFileSource;
class FrameBufferFilter;
class PassthroughFilter;
class DelayFilter;
class BlurFilter;
class ImageFilter;
class SourceCallback;
@@ -104,6 +105,7 @@ public:
virtual void visit (FrameBufferFilter&) {}
virtual void visit (PassthroughFilter&) {}
virtual void visit (DelayFilter&) {}
virtual void visit (BlurFilter&) {}
virtual void visit (ImageFilter&) {}
virtual void visit (SourceCallback&) {}

Binary file not shown.

View File

@@ -1,27 +1,29 @@
// 16x acceleration of https://www.shadertoy.com/view/4tSyzy
// by applying gaussian at intermediate MIPmap level.
vec3 texSample(const int x, const int y, in vec2 fragCoord)
{
vec2 uv = fragCoord.xy / iResolution.xy * iChannelResolution[0].xy;
uv = (uv + vec2(x, y)) / iChannelResolution[0].xy;
return texture(iChannel0, uv).xyz;
const int samples = 25,
LOD = 2, // gaussian done on MIPmap at scale LOD
sLOD = 1 << LOD; // tile size = 2^LOD
const float sigma = float(samples) * .25;
float gaussian(vec2 i) {
return exp( -.5* dot(i/=sigma,i) ) / ( 6.28 * sigma*sigma );
}
void blur( out vec3 rgb, in vec2 fragCoord )
{
vec3 tot = vec3(0.0);
vec4 blur(sampler2D sp, vec2 U, vec2 scale) {
vec4 O = vec4(0);
int s = samples/sLOD;
float t = 0., g;
for( int j=0; j<9; j++ )
for( int i=0; i<9; i++ )
tot += pow( texSample(i-4, j-4, fragCoord), vec3(2.2));
for ( int i = 0; i < s*s; i++ ) {
vec2 d = vec2(i%s, i/s)*float(sLOD) - float(samples)/2.;
t += g = gaussian(d);
O += g * textureLod( sp, U + scale * d , float(LOD) );
}
rgb = pow(tot/81.0,vec3(1.0/2.2));
return O / t;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec3 b;
blur(b, fragCoord);
fragColor = vec4( b, 1.0);
void mainImage(out vec4 O, vec2 U) {
O = blur( iChannel0, U/iResolution.xy, 1./iChannelResolution[0].xy );
}

View File

@@ -1,34 +1,26 @@
#define RADIUS 0.2
// Gaussian blur with mipmapping
// Bruno Herbelin
// Following tutorial https://www.shadertoy.com/view/WtKfD3
uniform float Amount;
#define N 13
float SCurve (float x) {
x = x * 2.0 - 1.0;
return -x * abs(x) * 0.5 + x + 0.5;
}
uniform float Radius;
vec3 BlurV (sampler2D source, vec2 uv, float step, float radius) {
vec3 C = vec3(0.0);
float divisor = 0.0;
float weight = 0.0;
float radiusMultiplier = 1.0 / max(1.0, radius);
// loop on pixels in Y to apply vertical blur
for (float y = -radius; y <= radius; y++) {
weight = SCurve(1.0 - (abs(y) * radiusMultiplier));
C += texture(source, uv + vec2(0.0, y * step)).rgb * weight;
divisor += weight;
vec4 blur1D(vec2 U, vec2 D, float rad)
{
float w = rad * iResolution.y;
float z = ceil(max(0.,log2(w/float(N)))); // LOD N/w = res/2^z
vec4 O = vec4(0);
float r = float(N-1)/2., g, t=0., x;
for( int k=0; k<N; k++ ) {
x = float(k)/r -1.;
t += g = exp(-2.*x*x );
O += g * textureLod(iChannel0, (U + w*x*D) / iResolution.xy, z );
}
return C / divisor;
return O/t;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
// downsample the image to benefit from (free) linear subsampling of colors
float subsampling = 1.0 - ( Amount * 0.9 );
vec2 uv = fragCoord.xy / ( iResolution.xy * subsampling );
// Apply vertical blur on downsampled image (allows to use smaller radius)
fragColor = vec4( BlurV(iChannel0, uv, 1.0 / (iResolution.y * subsampling), RADIUS * iResolution.y * Amount * subsampling), 1.0);
fragColor = blur1D( fragCoord, vec2(1,0), Radius * 0.5 );
}

View File

@@ -1,34 +1,24 @@
#define RADIUS 0.2
// Following tutorial https://www.shadertoy.com/view/WtKfD3
uniform float Amount;
#define N 13
float SCurve (float x) {
x = x * 2.0 - 1.0;
return -x * abs(x) * 0.5 + x + 0.5;
}
uniform float Radius;
vec3 BlurH (sampler2D source, vec2 uv, float step, float radius) {
vec3 C = vec3(0.0);
float divisor = 0.0;
float weight = 0.0;
float radiusMultiplier = 1.0 / max(1.0, radius);
// loop on pixels in X to apply horizontal blur: note optimization of +2 increment
for (float x = -radius; x <= radius; x+=2.0) {
weight = SCurve(1.0 - (abs(x) * radiusMultiplier));
C += texture(source, uv + vec2(x * step, 0.0)).rgb * weight;
divisor += weight;
vec4 blur1D(vec2 U, vec2 D, float rad)
{
float w = rad * iResolution.y;
float z = ceil(max(0.,log2(w/float(N)))); // LOD N/w = res/2^z
vec4 O = vec4(0);
float r = float(N-1)/2., g, t=0., x;
for( int k=0; k<N; k++ ) {
x = float(k)/r -1.;
t += g = exp(-2.*x*x );
O += g * textureLod(iChannel0, (U + w*x*D) / iResolution.xy, z );
}
return C / divisor;
return O/t;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
// upsample the image from blur_1
float upsampling = 1.0 - ( Amount * 0.9 );
vec2 uv = (fragCoord.xy * upsampling) / iResolution.xy;
// Apply horizontal blur on restored resolution image
fragColor = vec4( BlurH(iChannel0, uv, upsampling / iResolution.x, RADIUS * iResolution.y * Amount), 1.0);
fragColor = blur1D( fragCoord, vec2(0,1), Radius * 0.5 );
}

View File

@@ -0,0 +1,24 @@
#define TWOPI 6.28318530718
uniform float Radius;
uniform float Iterations;
float hash(vec2 co){
return fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453);
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = fragCoord.xy / iResolution.xy;
float R = 0.25 * Radius * iResolution.y / iResolution.x ;
vec4 O = vec4(0.);
float N = ( Iterations * 39.0 + 10.0 ); // between 10 and 50
for (float i = 0.; i < N; i++) {
vec2 q = vec2(cos(TWOPI*i/N), sin(TWOPI*i/N)) * hash(vec2(i, uv.x + uv.y));
O += pow( texture(iChannel0, uv + q*R), vec4(2.2) );
q = vec2(cos(TWOPI*((i+.5)/N)), sin(TWOPI*(i+.5)/N)) * hash(vec2(i + 2., uv.x * uv.y + 24.));
O += pow( texture(iChannel0, uv + q*R), vec4(2.2) );
}
fragColor = pow( O/(2.*N), vec4(1.0/2.2) );
}

View File

@@ -0,0 +1,25 @@
#define SEED uvec4(0x5C995C6Du, 0x6A3C6A57u, 0xC65536CBu, 0x3563995Fu)
const uint lcgM = 2891336453u;// ideal for 32 bits with odd c
uint asuint2(float x) { return x == 0.0 ? 0u : floatBitsToUint(x); }
uvec2 asuint2(vec2 x) { return uvec2(asuint2(x.x ), asuint2(x.y)); }
uvec3 asuint2(vec3 x) { return uvec3(asuint2(x.xy), asuint2(x.z)); }
uvec3 pcg3Mix(uvec3 h) {
h.x += h.y * h.z;
h.y += h.z * h.x;
h.z += h.x * h.y;
return h;
}
uvec3 pcg3Permute(uvec3 h) {
h = pcg3Mix(h);
h ^= h >> 16u;
return pcg3Mix(h);
}
uvec3 pcg3(uvec3 h, uint seed) {
uvec3 c = (seed << 1u) ^ SEED.xyz;
return pcg3Permute(h * lcgM + c);
}
float Float11(uint x) { return float(int(x)) * (1.0 / 2147483648.0); }
float Hash11(vec2 v, uint seed) { return Float11(pcg3(asuint2(vec3(v, 0.0)), seed).x); }
float Float01(uint x) { return float( x ) * (1.0 / 4294967296.0); }
float Hash01(vec2 v, uint seed) { return Float01(pcg3(asuint2(vec3(v, 0.0)), seed).x); }

View File

@@ -25,18 +25,18 @@ vec2 Hash22(vec2 p)
return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y));
}
vec3 Blur(vec2 uv, float radius)
vec4 Blur(vec2 uv, float radius)
{
radius = radius * .04;
vec2 circle = vec2(radius) * vec2((iResolution.y / iResolution.x), 1.0);
vec2 random = Hash22( uv + TIME );
// Do the blur here...
vec3 acc = vec3(0.0);
int max = int( Iterations * 49.0 + 1.0 ); // between 1 and 50
vec4 acc = vec4(0.0);
int max = int( Iterations * 29.0 + 1.0 ); // between 1 and 30
for (int i = 0; i < max; i++)
{
acc += texture(iChannel0, uv + circle * Sample(random), radius*10.0).xyz;
acc += textureLod(iChannel0, uv + circle * Sample(random), radius*10.0);
}
return acc / float(max);
}
@@ -45,8 +45,8 @@ void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = fragCoord.xy / iResolution.xy;
float radius = Radius * 0.005 * iResolution.y;
float radius = Radius * 0.01 * iResolution.y;
radius = pow(radius, 2.0);
fragColor = vec4(Blur(uv * vec2(1.0, -1.0), radius), 1.0);
fragColor = Blur(uv * vec2(1.0, -1.0), radius);
}

View File

@@ -0,0 +1,48 @@
#define TWOPI 6.28318530718
uniform float Radius;
// float hash(vec2 co){
// return fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453);
// }
#define SEED uvec4(0x5C995C6Du, 0x6A3C6A57u, 0xC65536CBu, 0x3563995Fu)
const uint lcgM = 2891336453u;// ideal for 32 bits with odd c
uint asuint2(float x) { return x == 0.0 ? 0u : floatBitsToUint(x); }
uvec2 asuint2(vec2 x) { return uvec2(asuint2(x.x ), asuint2(x.y)); }
uvec3 asuint2(vec3 x) { return uvec3(asuint2(x.xy), asuint2(x.z)); }
uvec3 pcg3Mix(uvec3 h) {
h.x += h.y * h.z;
h.y += h.z * h.x;
h.z += h.x * h.y;
return h;
}
uvec3 pcg3Permute(uvec3 h) {
h = pcg3Mix(h);
h ^= h >> 16u;
return pcg3Mix(h);
}
uvec3 pcg3(uvec3 h, uint seed) {
uvec3 c = (seed << 1u) ^ SEED.xyz;
return pcg3Permute(h * lcgM + c);
}
// float Float11(uint x) { return float(int(x)) * (1.0 / 2147483648.0); }
// float Hash11(vec2 v, uint seed) { return Float11(pcg3(asuint2(vec3(v, 0.0)), seed).x); }
float Float01(uint x) { return float( x ) * (1.0 / 4294967296.0); }
float Hash01(vec2 v, uint seed) { return Float01(pcg3(asuint2(vec3(v, 0.0)), seed).x); }
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = fragCoord.xy / iResolution.xy;
float ar = iResolution.y / iResolution.x ;
float R = 0.25 * Radius ;
vec4 O = vec4(1.);
float N = 17;
for (float i = 0.; i < N; i++) {
vec2 q = vec2(cos(TWOPI*((i+.5)/N)) * ar, sin(TWOPI*(i+.5)/N)) * Hash01(uv, floatBitsToUint(i));
// vec2 q = vec2(cos(TWOPI*((i+.5)/N)) * ar, sin(TWOPI*(i+.5)/N)) * hash(vec2(i + 12., uv.x * uv.y + 24.));
O = min( texture(iChannel0, uv + q*R), O );
}
fragColor = O;
}

View File

@@ -0,0 +1,49 @@
#define TWOPI 6.28318530718
uniform float Radius;
// float hash(vec2 co){
// return fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453);
// }
#define SEED uvec4(0x5C995C6Du, 0x6A3C6A57u, 0xC65536CBu, 0x3563995Fu)
const uint lcgM = 2891336453u;// ideal for 32 bits with odd c
uint asuint2(float x) { return x == 0.0 ? 0u : floatBitsToUint(x); }
uvec2 asuint2(vec2 x) { return uvec2(asuint2(x.x ), asuint2(x.y)); }
uvec3 asuint2(vec3 x) { return uvec3(asuint2(x.xy), asuint2(x.z)); }
uvec3 pcg3Mix(uvec3 h) {
h.x += h.y * h.z;
h.y += h.z * h.x;
h.z += h.x * h.y;
return h;
}
uvec3 pcg3Permute(uvec3 h) {
h = pcg3Mix(h);
h ^= h >> 16u;
return pcg3Mix(h);
}
uvec3 pcg3(uvec3 h, uint seed) {
uvec3 c = (seed << 1u) ^ SEED.xyz;
return pcg3Permute(h * lcgM + c);
}
// float Float11(uint x) { return float(int(x)) * (1.0 / 2147483648.0); }
// float Hash11(vec2 v, uint seed) { return Float11(pcg3(asuint2(vec3(v, 0.0)), seed).x); }
float Float01(uint x) { return float( x ) * (1.0 / 4294967296.0); }
float Hash01(vec2 v, uint seed) { return Float01(pcg3(asuint2(vec3(v, 0.0)), seed).x); }
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = fragCoord.xy / iResolution.xy;
float ar = iResolution.y / iResolution.x ;
float R = 0.25 * Radius ;
vec4 O = vec4(0.);
float N = 17;
for (float i = 0.; i < N; i++) {
vec2 q = vec2(cos(TWOPI*i/N) * ar, sin(TWOPI*i/N)) * Hash01(uv, floatBitsToUint(i) );
// vec2 q = vec2(cos(TWOPI*i/N) * ar, sin(TWOPI*i/N)) * hash(vec2(i, uv.x + uv.y));
O = max( texture(iChannel0, uv + q*R), O );
}
fragColor = O;
}

View File

@@ -0,0 +1,24 @@
#define N 13
uniform float Radius;
vec4 blur2D(vec2 U, float rad)
{
float w = rad * iResolution.y;
float z = ceil(max(0.,log2(w/float(N))));
vec4 O = vec4(0);
float r = float(N-1)/2., g, t=0.;
for( int k=0; k<N*N; k++ ) {
vec2 P = vec2(k%N, k/N) / r - 1.;
if ( dot(P,P) < 1.0 ) {
t += g = exp(-2.*dot(P,P) );
O += g * textureLod(iChannel0, (U + w*P) / iResolution.xy, z );
}
}
return O/t;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
fragColor = blur2D( fragCoord, Radius * 0.25 );
}

View File

@@ -0,0 +1,34 @@
#define RADIUS 0.2
uniform float Amount;
float SCurve (float x) {
x = x * 2.0 - 1.0;
return -x * abs(x) * 0.5 + x + 0.5;
}
vec3 BlurV (sampler2D source, vec2 uv, float step, float radius) {
vec3 C = vec3(0.0);
float divisor = 0.0;
float weight = 0.0;
float radiusMultiplier = 1.0 / max(1.0, radius);
// loop on pixels in Y to apply vertical blur
for (float y = -radius; y <= radius; y++) {
weight = SCurve(1.0 - (abs(y) * radiusMultiplier));
C += texture(source, uv + vec2(0.0, y * step)).rgb * weight;
divisor += weight;
}
return C / divisor;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
// downsample the image to benefit from (free) linear subsampling of colors
float subsampling = 1.0 - ( Amount * 0.9 );
vec2 uv = fragCoord.xy / ( iResolution.xy * subsampling );
// Apply vertical blur on downsampled image (allows to use smaller radius)
fragColor = vec4( BlurV(iChannel0, uv, 1.0 / (iResolution.y * subsampling), RADIUS * iResolution.y * Amount * subsampling), 1.0);
}

View File

@@ -0,0 +1,34 @@
#define RADIUS 0.2
uniform float Amount;
float SCurve (float x) {
x = x * 2.0 - 1.0;
return -x * abs(x) * 0.5 + x + 0.5;
}
vec3 BlurH (sampler2D source, vec2 uv, float step, float radius) {
vec3 C = vec3(0.0);
float divisor = 0.0;
float weight = 0.0;
float radiusMultiplier = 1.0 / max(1.0, radius);
// loop on pixels in X to apply horizontal blur: note optimization of +2 increment
for (float x = -radius; x <= radius; x+=2.0) {
weight = SCurve(1.0 - (abs(x) * radiusMultiplier));
C += texture(source, uv + vec2(x * step, 0.0)).rgb * weight;
divisor += weight;
}
return C / divisor;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
// upsample the image from blur_1
float upsampling = 1.0 - ( Amount * 0.9 );
vec2 uv = (fragCoord.xy * upsampling) / iResolution.xy;
// Apply horizontal blur on restored resolution image
fragColor = vec4( BlurH(iChannel0, uv, upsampling / iResolution.x, RADIUS * iResolution.y * Amount), 1.0);
}