From e3bb95b3dd699a7c3038636caa3f9a2d82c54e04 Mon Sep 17 00:00:00 2001 From: Bruno Herbelin Date: Tue, 31 May 2022 00:34:37 +0200 Subject: [PATCH] Original implementation of Blur Image Filters With Gaussian, fast Gaussian, Hashed and morphological (opening and closing) methods. Remembering shader code for other fast methods. --- CMakeLists.txt | 3 + CloneSource.cpp | 5 +- FrameBuffer.cpp | 63 +++------ FrameBuffer.h | 4 +- FrameBufferFilter.cpp | 2 +- FrameBufferFilter.h | 1 + ImGuiVisitor.cpp | 59 +++++++-- ImGuiVisitor.h | 1 + ImageFilter.cpp | 167 ++++++++++++++++++++++-- ImageFilter.h | 56 +++++++- SessionCreator.cpp | 24 ++++ SessionCreator.h | 1 + SessionVisitor.cpp | 16 +++ SessionVisitor.h | 1 + Visitor.h | 2 + rsc/images/icons.dds | Bin 1638528 -> 1638528 bytes rsc/shaders/filters/blur.glsl | 44 ++++--- rsc/shaders/filters/blur_1.glsl | 44 +++---- rsc/shaders/filters/blur_2.glsl | 40 +++--- rsc/shaders/filters/circularblur.glsl | 24 ++++ rsc/shaders/filters/hash.glsl | 25 ++++ rsc/shaders/filters/hashedblur.glsl | 12 +- rsc/shaders/filters/hasheddilation.glsl | 48 +++++++ rsc/shaders/filters/hashederosion.glsl | 49 +++++++ rsc/shaders/filters/mipmapblur.glsl | 24 ++++ rsc/shaders/filters/sigmoid_1.glsl | 34 +++++ rsc/shaders/filters/sigmoid_2.glsl | 34 +++++ 27 files changed, 628 insertions(+), 155 deletions(-) create mode 100644 rsc/shaders/filters/circularblur.glsl create mode 100644 rsc/shaders/filters/hash.glsl create mode 100644 rsc/shaders/filters/hasheddilation.glsl create mode 100644 rsc/shaders/filters/hashederosion.glsl create mode 100644 rsc/shaders/filters/mipmapblur.glsl create mode 100644 rsc/shaders/filters/sigmoid_1.glsl create mode 100644 rsc/shaders/filters/sigmoid_2.glsl diff --git a/CMakeLists.txt b/CMakeLists.txt index cc0aec9..565d6cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/CloneSource.cpp b/CloneSource.cpp index be22f95..2a63827 100644 --- a/CloneSource.cpp +++ b/CloneSource.cpp @@ -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 ? } diff --git a/FrameBuffer.cpp b/FrameBuffer.cpp index 2c19882..707b1b5 100644 --- a/FrameBuffer.cpp +++ b/FrameBuffer.cpp @@ -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 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) <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 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 diff --git a/ImGuiVisitor.h b/ImGuiVisitor.h index 3f94481..f127167 100644 --- a/ImGuiVisitor.h +++ b/ImGuiVisitor.h @@ -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; }; diff --git a/ImageFilter.cpp b/ImageFilter.cpp index d5a18a8..9aad120 100644 --- a/ImageFilter.cpp +++ b/ImageFilter.cpp @@ -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 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 > ¶meters) +{ + 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(), mipmap_buffer_->projection()); + mipmap_buffer_->end(); + + // FIRST PASS + // render mipmapped texture into frame buffer + buffers_.first->begin(); + surfaces_.first->draw(glm::identity(), 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(), buffers_.second->projection()); + buffers_.second->end(); + } + } +} + +void BlurFilter::accept (Visitor& v) +{ + FrameBufferFilter::accept(v); + v.visit(*this); +} + + + diff --git a/ImageFilter.h b/ImageFilter.h index f7b83c3..7d1068c 100644 --- a/ImageFilter.h +++ b/ImageFilter.h @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -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 *ret = nullptr); - // get copy of the program FilteringProgram program() const; + // update parameters of program + void setProgramParameters(const std::map< std::string, float > ¶meters); + 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 diff --git a/SessionCreator.cpp b/SessionCreator.cpp index 524b1af..0be6ff4 100644 --- a/SessionCreator.cpp +++ b/SessionCreator.cpp @@ -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; diff --git a/SessionCreator.h b/SessionCreator.h index 1703c55..dd4475b 100644 --- a/SessionCreator.h +++ b/SessionCreator.h @@ -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 diff --git a/SessionVisitor.cpp b/SessionVisitor.cpp index 0968526..232feb1 100644 --- a/SessionVisitor.cpp +++ b/SessionVisitor.cpp @@ -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() ); diff --git a/SessionVisitor.h b/SessionVisitor.h index e6fa10d..1fb974f 100644 --- a/SessionVisitor.h +++ b/SessionVisitor.h @@ -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 diff --git a/Visitor.h b/Visitor.h index 14f019b..7b5b6be 100644 --- a/Visitor.h +++ b/Visitor.h @@ -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&) {} diff --git a/rsc/images/icons.dds b/rsc/images/icons.dds index d7bfd288ec6dbd99794269ed1e60b46d3b96424d..cf10a2aea8260985e04e695e30066ad6d6039e2f 100644 GIT binary patch delta 10343 zcmdTq3v^V~wddSBcar($&P*nVKr;bEevt{_0{NI5s0CXI17<{9fy7cQmTG9BeqLc_ zR0?#JM~2aNn`5>3fHd|b;{kkCUMdl zbPjZnWc}d-jYr{VBCkj8mb)WovWI3Cqh^6+u>8oqSJtS+m(qwM)z8e&yd5=QW@ID( zRgCl5*_IViqb{9z4?FviagxC{_?HRsp#GZw)f-n*mr5RMlYdh8)PwsSMeLe8uvz8t||*q&r@3psUE z@Lr7?VB1)83RMKuNX@d_$273vx}0sN`}#0KOLUmAIJ?Xe>GG~-65#TBvDL>E96(Wt z&0tZz-`j)gkXJ%F%qBI96A(_ps2siPjUPzSs}ISaq_wJaogT(pM4ULUE>wJz97t0m z3L>K#9VZizfSisGKTIP-4*X%?bQn3%2kEdQ4ZoGOvVBY)=4voh?ormP<8%Ik2zEdY zJr=3@uEEg8S{QlmX+wVz=aaiJVFZQg|3Iy^@>oRw4c8rQk6)iziC}m7k(g1&fLz*nK!PE4IiEy?K$7q1GxR+-6KH1&Rj45%1I7o_0R zrBD?U;DO=kd?EQ&4gWUZkqzQHUOl04xky3pDrpIRKZ+XIyxsxg;RB@HpqA6G)H7*R zk{{j@4wgkiug)YDPNSjjz%M?`A{}>2*(Ce8G$5xx3_Z1G>m_7rPdj5giVpR)D!{l6 zPew+bxof18s2X{Q@whZ2@?~~*8j`dP=U<03f}CG$7#z8n?fhy?ugVUk5`yd8!%p%) zS`4hH?M}@`ZbK;wEn>)uh7zRE!EODuAzGTTB_PE(?6zq;$NcrDp?hE zp;vQd+Sp}k70HsRR!?2{y{xO@BiF#~SHUzaVEqMMl7mMjiRqHzz0Y8j2h(sN&dv<1 zN2&40eQ^$wT=yx5+rMVm;E_D({0$#U@rTLFU8b${qv?3BlT>`3Q9e`xEdJ3koF%IAkO_L^@{= zCwW!Y98+p#6b_D|k*BfErK&g`5MfaFOC{jh_R(Xw!*tf|?PLBvJaUY{@gvVYa2pLj zhqE-k^0qrT2d!~aNySaBf-I4ZWcl!c#$SemgXmMQ-~lR4)q1xhQXTWsP$SO0=FNfY z-kiDaP0w|2WZ^14g|uR@Ag4H!Y7r|mJYLXL=HmIQnKe~+2`Qv3u5Etrj1vy}==M_K zyfbhaJ(=z&#*QBScf#ZUX@RD->hOkKdyKX$72dZ~veq$Ap!Hp*3X!f{BM5f7s@hR3 zgjYJ~sSUz!9W+$$$iaN&JqgqOe-MsJL^g>A!Sg76txh;%Ar&7uIwKEWd4yhS6t*}? z)ltXsaHxbHJRtnVNw%MNw1z|9rn}Dw-A>XdIh(>Eg*NqYf4B5?){>^_CV*uTLeiS= z+^)hkO@l=jX)1Cys>tKT4kxMgJ6pn`nY86DvDHbc7dhL)q1kkQskqZg!pof<;gFBE zE)};sX(;TJaX3^+TULvo8japZOIazSudfq7u+Y$E=S%@AE8sICz*9hMX_-4|^dee~m=&3!nnIgT_X|Bce)C@ce({XNd&_?Mew#6bHG{pQ2 zxagSe2}}!ynU~t=<}^mgW;7UsAqzCB_^9|t!;GWB(oF36}NeXK$%9U_>e5uzmVWXVQ#FYhq0fZ zO{l|dV`pV@!dF4|x8Ofd)5?h6kI@fsYyw|~RK~E|@87}XE>xD2R19ZvZV5)dlVRT! zUimF57K7o0tihktkk2gJX}FJl0Tz+6Au@eVZeA%z=VKh<(pO;Bl(z0RpcN=8lJQ5Z z%LB3j5V>O%MeYi-E=~n-iQn&6AK^#W@9T!wVb-@lY@DD~#=u^GDHx1vjf$oa#>Cgp zzLJ~zWtn*+CgDna$866^j<4E7K_^rV6rYdN+>f?l6_zA7@r|@cN_^;`SDg#E z8V8aKdYvsSSS2<$ZJVCj%4=#z2gUATX_H~xl(0k%Gsw7h>l1+k@)cjA7Fbord?h8{ zcm%l4yy+44XkhP)$zVIokNvnplH2VkzESpNJR&ESng4)EMJ>K*qJq4fObtoVvu6_x zi3S}$#A5diK?bD5JBO%KRSFIVJ>zB{K^`6m9fwsu8%MKIkq$!`AL@?vs3S@XI|hy- znKsJ)1Z{fE+-M`A-FRVNS&b3oF=sYQxRR;C?t|fApWE(Ec9y`Hea#z3hd5jg9!5cf zd?V@u(ko-LiX_`&2slC>2>9GsVFoHBKEbxi0lYN>J>BTlJkJLP()BaxHaW^>M5TEt z%?;%IM%hDV3AU1eoDWXXhSg`R6IR@bl#@9x-gKvH(ANu3`nv*(SpYU#Fk{xeeVvyY_5e`BtKFz49r z45&36#--LYl|h)Q9oMfira)Iu|D__w;IQ9s)kRxPg@=fFqR9aJhsry)$)~UrY?Kc^oL;& z612us)zfWwb%rOZX3J30(O{HB*b72+9!6&6*n9z>x!e>q1-VcKnog?6<(r28X@OEx zMpb4-W(8LmLnEbg?04`)d8g966B6YxE)1M7x8`r39}IBiwc)BdA3UkSJ7V&Ce8B7Q za1h@g&F7&%(O2VXYj(s6V`bA!&x$#s+X1oHFsufdT)TCyoxIfgCO=DJd6W>D@==H#a6`WhL1cX%!zKx13!! zZ{N1^p(?)LUsAeNc5x()9*gj-$Y*5GAAABe8+8i%P0D1o-#5!I%|~~k{$PNIBS;cf zfEG=`Uq~DV{xGEsw=KyC%U8ne%rqT#o5v{H@w?Q8L*sf%3^6Zp9L7$V%??}VseMtT2 zhTl$_UJ?d7W$skX6ZK9pY#&k# zfG*>A)#*n<*?H>02N8Tg3myz8H6j zXT+$ls4FJf2plx)8-$67v(({mFptc-9kRk(EU!b&YA!1ZLJ63MI@xF6=*+`)&*vvV!dD7hfg5&Zh#jUIR7aP*r(DLcAJkuR!p`jN*B6_P9P{in&BD9 zj3)ELyClrr%3BKl6nzT#u$iv!pj*fb$jFC0TCn)QdPzuR95oIC39w{!`)^gnsS?$R3DV0ueOE)pHr@~Oj2|va zJQ$`HzPadV5ygpon(?6^@0W_o%aJ?=z0H?c04uP<>dt@f1`wz6=h@1iXL-Ny!p;H& z@cIG_BrscrF_wsLwk;^a6i!^#rorU&RJ+X3fj*p{Us$( zE3~FN{~Lz$bpE~B&@Tq(idw1kyWL>Q-L|PTa@ZV)m8DxK0N<;P6Y_f3HFrrnI)xM+ zMkogbaJ=^d0V0jQL>Kh_{-{3`cT36@z|1F~em<)AzuOQWwgUIwgQzP8K+^l+WRqX! z6wuHo<}5p@7$%Gf_;|5Ug9XX33@4=aj9XRM4b79S>g(YB5U3Ks{Rnj-IDLoUU%1dA zc>2M4wp+z7xrO7K^#v6)fo`LMljbCcJD8)?RGc#l+}XZ@ceZ)E(eu5A2^U6O zXzseK?y`A8n3Wq3{<=Lemn+TT9@6@$!ApwDZByt|9cH_ogeMDk-m^T$i};eoyPC@v z%SquS2lxUHDz}#_;QY9P7WB2iVNUHKdy7Cr!z^B*rnXw^ z>!8*7mO>kCJ8Q3ozR4CiFlu{TpqP>(i(=vDVs=bV7F+&`>Bo0j>;i$KvFy2!2y?t zwEWJ}Mz>d4TE&{s%NCfr#)@zXx7pHWrkiUmJ)lV;Y1?jTqSZSrZNRKu@RHg;0!{c0 zi)^9QyDeTZ9Lgpq-?jWP9P-lEJ(lf&w$D;Zj<;CgU@n)m%&{5hyMMAAHPh@mdU+K>2T0XO3n6C+IZY@9O%wjJR)g2Yk|`|kkO&iby+F` zF?Lt8SnSa=C`b10Ihb~4eh)t}pOkjM0cCYeU3+R&ejA_r0jK4SkIE$p{88W1O{t7z zh@k}POu+!EA03}mfIn#CMO8;^b&Ai{S=%Gk4}gvaEB8aYFqYI?t;K_z;nFU3tZwAv z8JfE%N z)}4A-JXW6~@j8Lj36mRy6!N-iZPGyfS(nTjVtoQI_vr8&Z6QS#_m@%yxOfHi8S+lN^(C^gi|==PCTxWx&LwFWbMbCl+Sn2Q|RDGVjP zN^F?gUtNo8Y*h!qpwq>iU?)Imwch#o7V_ z@5%@UjLfvb*z46PFCrz~%lt(03M?s89LH;kp~H?HZ>8Mxl^U#R!1ItP<*PfBCg}7u zc!w1RARH58Oq*s~8LnycfrgDG*1HWQp7ct08FKWxtL}Dov(AeDZBL9jnGW-3-neQb z&^ojYO`s3nXZ^lj7#3QgFf6dPVOqV&8W4-*_w~iTMza+|jf1B1K;f(VV?Vi7%}Tu= z6UQcTG>!b!I>SQ1sN=Nh32QD?wa2W-!R=k9EKJ~PVCpJYzzSa*eC^Z~$ojIC7>luk z^eHjU5S9BrqI2#s?iC_sSC-J{OJui>NVjcdIM_UNLJZ{X>piQd?&Vc5oQ)cdXA zbxfE2NN#kE-jqna9^vV?yX+62rQkZ|>K^#~?m;@)He3vg<(tWaQ>`OulQ%zJ6j z=qzx-BBdOiQc|bSUBGqJ(7-kS8seKIr-HX=foi~25ZMaIvqj_e0&C1`6vmv=l2;SAopoC!gc0rWgzcm?%R0A4!sZ_c-%h>KG!f zAKLPX_lGw5njgH7;9YR96?(`C_gWCzj+)R4r;7(!hP&$Z&JHJko+;3l8fTLl{Q1JF aj<=lfb-_0qzHa#Dz_$~3<;-Ob&*o1ik74KPtCltcs^#U|lltymxep>!xDOoWxG^z>J;fYU>`!J%(tv(5%o5=U1AAIk6j*8Kl}&lp&X-Z z6tcNrau6m~-ryI`=l7K*5;CFF{0sn~bS=G~eMtIHd6Q8)(&iP8s~3v;$;obQgv~-n z(Q3M%{0O7n*3IfyMg89T6YlF>r|ATqku&H_ql0P1g%;Ci0VWYz)r~x01P_k=M`n3CA=AL}SnkAK`dDFZ}{l`Mo zN-N^JkNT*W7WK#V<)tHs9(|j!WrN2TELDe=AHAQerv7!bC)(dh>*BiMon-yi#mi_J zok=}?@nk=lGjVM6G}mIq=FAy8_H>H=6)(R3zf_zv_t9kEqy2TE7AGAUFJ4zXsH?c| zU;<2Eltx)!8shf9^*n^^FUx zsJp(lWS+UpsXNUA5`*t#c5lUjXlZiidCESzD$)F((+|1-tNe@m)7z=-DP$4iy5TMQ zH>z2CD*VyBlxi$ZFD*AqnAv$+diq)ZZR(?=)(Ro*TUN{Kt2iEgfUZnr+s~!u{g4#oik@hIJ{8a2@-CN7@~;>dVv1(}&1wtib^E1T$+ADC zj|#1tK1_}6SbDq;N&kZ~B4^S{eqkmkl)3~qPzuDE$@X6ontO5RSoAN2p8t*IMo;#N zi(?<+ciVqnmdECf-&UcfLf$*ic~LAf^!$2#^NdjYPunw-;B6-FA`5|#I5LNI>A?qu z1Wk*OAy`Xq0TNmc|S=&t&YJ2!bTY>~mSJ|BFGK z+{{qN<21PmL1~Rrf?z_H?6t?b!eK9hVR`cJ5O2ya5fqG&SJ+{7 zl^aXkSP7n6<<*ER!6*W&C(7DDJ7a;;bQVaKVe~2JGiB8g!)Ewq!P#cOYonBELRhBJC^XEO>FJeAvX1he%*u#$9k`m)sBoL%cUTEU@+ixfgLZ zXFxwJXF5!r0pH(babU4~fY+BI)FLd49A6{rlLc4Wxf~He&%z#a-N)9i#jLq(y`8O( z!1?BSH*3OZTxjRC{cQbq%#I(E0Rbt-MHt?jfgK`r{Sbd{?!!I=FWiHNIiPJ3uh>zJ zzeK>=%ns1;C3w9^7Gcy6`n^1~^+9Yk_u%V5YZVq*PjOg^9X$Ql%z?B%fxieeKZ+%2 zeiHAEnTUQ~EQR+Bd`LF*9Max^BZ1(v%q7@}JCZI_VCRcCA{(o3Baskp4fuo1#lHnd z;ssvA9&G4SNLL$v6Z|`Hz62e+a4?ynkhIUWr@@YW_z0_%N7_Ebhrs_n&Xb_+Fn&G7 z5yHptG3MhTtx+5S|7X}EL2GxM3`blD;d9I<;FlEC>{m32jNPqX4;^VrqT)np61
}6xMaOqDs(ojnW=RJVY6T1HtbwTku8&G25u5tw71gfu=$Q1)3%*%v6*#O;rkU zAUunOv7^GT=@Nw^2+vk#rSZ+E0DYdqH)EL+VgIOm03ayk$aVD^HRDWjQf-s z7$Uz?j>#F=thLyrY6_AsuecvY&yVIoq4u{Sa;eZ{E~fBgy&|fSt@~LRxg?G zf&DJfm#AA@WZWax`CvS(Hn>Q>&sq+~8g;3QwAENM(PzZwS4WVt5{pWTvK+-k??Kr> z{OaAVoA=Y{>_-na`0LbiP2$gx#6u+a&q98xPU!{uF>NA)n$_FLJ=?5>#Q4}*XZW0? z;5ug=gtw}>T4p-6(ez|a!|Ng48>BSJOPgTOZgr3ArUkT=K6?ChX?=r8_OsR?J-_`0s9jrBK?0+KCF-!^q zTjIVGB<4Gb`I4q-Qak9Sn#V=_3#AC?KhxMV)A)td9ne>5F3@Wwc3U={*4Sf7Unk8F kp*^6rI>Fc^Efj0J-_Y1}vFT> 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); } \ No newline at end of file diff --git a/rsc/shaders/filters/hashedblur.glsl b/rsc/shaders/filters/hashedblur.glsl index d94ec64..d02a07e 100644 --- a/rsc/shaders/filters/hashedblur.glsl +++ b/rsc/shaders/filters/hashedblur.glsl @@ -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); } diff --git a/rsc/shaders/filters/hasheddilation.glsl b/rsc/shaders/filters/hasheddilation.glsl new file mode 100644 index 0000000..df7cef5 --- /dev/null +++ b/rsc/shaders/filters/hasheddilation.glsl @@ -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; +} diff --git a/rsc/shaders/filters/hashederosion.glsl b/rsc/shaders/filters/hashederosion.glsl new file mode 100644 index 0000000..0d0592c --- /dev/null +++ b/rsc/shaders/filters/hashederosion.glsl @@ -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; +} diff --git a/rsc/shaders/filters/mipmapblur.glsl b/rsc/shaders/filters/mipmapblur.glsl new file mode 100644 index 0000000..7cab2e8 --- /dev/null +++ b/rsc/shaders/filters/mipmapblur.glsl @@ -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