From 80469ead188372a787ac183732adf79e23f1467a Mon Sep 17 00:00:00 2001 From: Bruno Herbelin Date: Sun, 1 May 2022 22:24:59 +0200 Subject: [PATCH] Initial commit of ImageFilter shader presets Clone source can choose a filter --- CMakeLists.txt | 20 +- CloneSource.cpp | 41 ++- ImGuiVisitor.cpp | 41 +++ ImageFilter.cpp | 411 +++++++++++++++++---------- ImageFilter.h | 53 +++- Resource.cpp | 2 +- SessionVisitor.cpp | 26 +- SessionVisitor.h | 1 + UserInterfaceManager.cpp | 25 +- UserInterfaceManager.h | 2 + Visitor.h | 2 + rsc/shaders/filters/bloom.glsl | 31 ++ rsc/shaders/filters/blur.glsl | 27 ++ rsc/shaders/filters/blur_1.glsl | 33 +++ rsc/shaders/filters/blur_2.glsl | 34 +++ rsc/shaders/filters/bokeh.glsl | 42 +++ rsc/shaders/filters/chromakey.glsl | 58 ++++ rsc/shaders/filters/default.glsl | 5 + rsc/shaders/filters/denoise.glsl | 141 +++++++++ rsc/shaders/filters/edge.glsl | 22 ++ rsc/shaders/filters/fisheye.glsl | 31 ++ rsc/shaders/filters/focus.glsl | 52 ++++ rsc/shaders/filters/freichen.glsl | 44 +++ rsc/shaders/filters/hashedblur.glsl | 52 ++++ rsc/shaders/filters/kuwahara.glsl | 100 +++++++ rsc/shaders/filters/pixelate.glsl | 24 ++ rsc/shaders/filters/sharpen.glsl | 50 ++++ rsc/shaders/filters/sharpenedge.glsl | 43 +++ rsc/shaders/filters/sketch.glsl | 90 ++++++ rsc/shaders/filters/sobel.glsl | 36 +++ rsc/shaders/filters/talk.glsl | 46 +++ rsc/shaders/image.fs | 2 +- 32 files changed, 1385 insertions(+), 202 deletions(-) create mode 100644 rsc/shaders/filters/bloom.glsl create mode 100644 rsc/shaders/filters/blur.glsl create mode 100644 rsc/shaders/filters/blur_1.glsl create mode 100644 rsc/shaders/filters/blur_2.glsl create mode 100644 rsc/shaders/filters/bokeh.glsl create mode 100644 rsc/shaders/filters/chromakey.glsl create mode 100644 rsc/shaders/filters/default.glsl create mode 100644 rsc/shaders/filters/denoise.glsl create mode 100644 rsc/shaders/filters/edge.glsl create mode 100644 rsc/shaders/filters/fisheye.glsl create mode 100644 rsc/shaders/filters/focus.glsl create mode 100644 rsc/shaders/filters/freichen.glsl create mode 100644 rsc/shaders/filters/hashedblur.glsl create mode 100644 rsc/shaders/filters/kuwahara.glsl create mode 100644 rsc/shaders/filters/pixelate.glsl create mode 100644 rsc/shaders/filters/sharpen.glsl create mode 100644 rsc/shaders/filters/sharpenedge.glsl create mode 100644 rsc/shaders/filters/sketch.glsl create mode 100644 rsc/shaders/filters/sobel.glsl create mode 100644 rsc/shaders/filters/talk.glsl diff --git a/CMakeLists.txt b/CMakeLists.txt index e50e1f9..c60a5aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -555,7 +555,25 @@ set(VMIX_RSC_FILES ./rsc/mesh/icon_sequence.ply ./rsc/mesh/icon_receive.ply ./rsc/mesh/h_line.ply - ./rsc/mesh/h_mark.ply + ./rsc/mesh/h_mark.ply + ./rsc/shaders/filters/default.glsl + ./rsc/shaders/filters/bloom.glsl + ./rsc/shaders/filters/bokeh.glsl + ./rsc/shaders/filters/talk.glsl + ./rsc/shaders/filters/fisheye.glsl + ./rsc/shaders/filters/hashedblur.glsl + ./rsc/shaders/filters/blur.glsl + ./rsc/shaders/filters/blur_1.glsl + ./rsc/shaders/filters/blur_2.glsl + ./rsc/shaders/filters/sharpen.glsl + ./rsc/shaders/filters/sharpenedge.glsl + ./rsc/shaders/filters/chromakey.glsl + ./rsc/shaders/filters/kuwahara.glsl + ./rsc/shaders/filters/freichen.glsl + ./rsc/shaders/filters/sobel.glsl + ./rsc/shaders/filters/pixelate.glsl + ./rsc/shaders/filters/focus.glsl + ./rsc/shaders/filters/denoise.glsl ) # Include the CMake RC module diff --git a/CloneSource.cpp b/CloneSource.cpp index c442b80..16fe26e 100644 --- a/CloneSource.cpp +++ b/CloneSource.cpp @@ -84,8 +84,11 @@ void CloneSource::init() timestamps_.push( origin_->playtime() ); elapsed_.push( 0. ); - // ask to reset elapsed-timer - timer_reset_ = true; + // create render Frame buffer matching size of images + FrameBuffer *renderbuffer = new FrameBuffer( res, true ); + + // set the renderbuffer of the source and attach rendering nodes + attach(renderbuffer); // create filter for this resolution (with alpha channel) filter_render_ = new ImageFilterRenderer( res ); @@ -93,18 +96,12 @@ void CloneSource::init() // provide initial texture to filter filter_render_->setInputTexture( images_.front()->texture() ); - // set texture surface to draw the result of the filter - texturesurface_->setTextureIndex( filter_render_->getOutputTexture() ); - - // create render Frame buffer matching size of images - FrameBuffer *renderbuffer = new FrameBuffer( res, true ); - - // set the renderbuffer of the source and attach rendering nodes - attach(renderbuffer); - // force update of activation mode active_ = true; + // ask to reset elapsed-timer + timer_reset_ = true; + // deep update to reorder ++View::need_deep_update_; @@ -118,8 +115,11 @@ void CloneSource::render() if ( renderbuffer_ == nullptr ) init(); else { - // render filtered image + + // render filter image filter_render_->draw(); + + // ensure correct output texture is displayed (could have changed if filter changed) texturesurface_->setTextureIndex( filter_render_->getOutputTexture() ); // render textured surface into frame buffer @@ -177,10 +177,12 @@ void CloneSource::update(float dt) { // remember FBO to be reused if needed (see below) or deleted later garbage_image_ = images_.front(); + // remove element from queue (front) images_.pop(); elapsed_.pop(); timestamps_.pop(); + } // add image to queue to accumulate buffer images until delay reached @@ -199,6 +201,7 @@ void CloneSource::update(float dt) garbage_image_ = nullptr; } else { + // set delay to maximum affordable delay_ = now - elapsed_.front() - (dt * 0.001); Log::Warning("Cannot satisfy delay for Clone %s: not enough RAM in graphics card.", name_.c_str()); } @@ -210,7 +213,6 @@ void CloneSource::update(float dt) origin_->frame()->blit( images_.back() ); // update the surface to be rendered with the oldest image (front) -// texturesurface_->setTextureIndex( images_.front()->texture() ); filter_render_->setInputTexture( images_.front()->texture() ); } @@ -231,12 +233,21 @@ void CloneSource::setDelay(double second) void CloneSource::setFilter(const ImageFilter &filter, std::promise *ret) { - filter_render_->setFilter(filter, ret); + +// filter_shader_->setFilter(filter, ret); + if (filter_render_) + filter_render_->setFilter(filter, ret); } ImageFilter CloneSource::filter() const { - return filter_render_->filter(); +// return filter_shader_->filter(); + + if (filter_render_) + return filter_render_->filter(); + + ImageFilter f; + return f; } void CloneSource::play (bool on) diff --git a/ImGuiVisitor.cpp b/ImGuiVisitor.cpp index eed2ae3..8a58b5c 100644 --- a/ImGuiVisitor.cpp +++ b/ImGuiVisitor.cpp @@ -784,6 +784,47 @@ void ImGuiVisitor::visit (CloneSource& s) Action::manager().store(oss.str()); } + ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); + if (ImGui::BeginCombo("##Filters", s.filter().name().c_str()) ) + { + for (auto f = ImageFilter::presets.begin(); f != ImageFilter::presets.end(); ++f){ + if (ImGui::Selectable( f->name().c_str() )) { + // apply the selected filter to the source + s.setFilter( *f ); + // ask code editor to refresh + UserInterface::manager().showSourceEditor(&s); + + // TODO UNDO std::ostringstream oss; + // oss << s.name() << ": Pattern " << Pattern::get(p).label; + // Action::manager().store(oss.str()); + } + } + ImGui::EndCombo(); + } + ImGui::SameLine(); + ImGui::Text("Filter"); + + std::map filter_parameters = s.filter().parameters(); + ImageFilter target = s.filter(); + for (auto param = filter_parameters.begin(); param != filter_parameters.end(); ++param) + { + // TODO VISIT ImageFilter + float f = param->second; + ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); + if (ImGui::SliderFloat( param->first.c_str(), &f, 0.f, 1.f, "%.2f")) { + target.setParameter(param->first, f); + s.setFilter( target ); + } + if (ImGui::IsItemDeactivatedAfterEdit()) { + // TODO UNDO + // std::ostringstream oss; + // oss << "Delay " << std::setprecision(3) << d << " s"; + // Action::manager().store(oss.str()); + } + + } + + } void ImGuiVisitor::visit (PatternSource& s) diff --git a/ImageFilter.cpp b/ImageFilter.cpp index ee26743..088ee14 100644 --- a/ImageFilter.cpp +++ b/ImageFilter.cpp @@ -1,4 +1,4 @@ -/* +/* * This file is part of vimix - video live mixer * * **Copyright** (C) 2019-2022 Bruno Herbelin @@ -23,7 +23,8 @@ #include #include "defines.h" -#include "Shader.h" +#include "Resource.h" +#include "ImageShader.h" #include "Visitor.h" #include "FrameBuffer.h" #include "Primitives.h" @@ -31,15 +32,6 @@ #include "ImageFilter.h" -std::string filterHeaderHelp = "vec3 iResolution; // viewport resolution (in pixels)\n" - "float iTime; // shader playback time (in seconds)\n" - "float iTimeDelta; // render time (in seconds)\n" - "int iFrame; // shader playback frame\n" - "vec3 iChannelResolution[2]; // input channel resolution (in pixels)\n" - "sampler2D iChannel0; // input channel (texture).\n" - "sampler2D iChannel1; // input channel (mask).\n" - "vec4 iDate; // (year, month, day, time in seconds)"; - std::string fragmentHeader = "#version 330 core\n" "out vec4 FragColor;\n" "in vec4 vertexColor;\n" @@ -57,11 +49,11 @@ std::string fragmentHeader = "#version 330 core\n" "uniform vec4 iDate;\n"; // Filter code starts at line 16 : -std::string filterDefault = "void mainImage( out vec4 fragColor, in vec2 fragCoord )\n" - "{\n" - " vec2 uv = fragCoord.xy / iResolution.xy;\n" - " fragColor = texture(iChannel0, uv);\n" - "}\n"; +std::string filterDefault = "void mainImage( out vec4 fragColor, in vec2 fragCoord )\n" + "{\n" + " vec2 uv = fragCoord.xy / iResolution.xy;\n" + " fragColor = texture(iChannel0, uv);\n" + "}\n"; std::string fragmentFooter = "void main() {\n" " iChannelResolution[0] = vec3(textureSize(iChannel0, 0), 0.f);\n" @@ -70,46 +62,153 @@ std::string fragmentFooter = "void main() {\n" " mainImage( FragColor, texcoord.xy * iChannelResolution[0].xy );\n" "}\n"; +//std::string fragmentFooter = "void main() {\n" +// " iChannelResolution[0] = vec3(textureSize(iChannel0, 0), 0.f);\n" +// " iChannelResolution[1] = vec3(textureSize(iChannel1, 0), 0.f);\n" +// " vec4 texcoord = iTransform * vec4(vertexUV.x, vertexUV.y, 0.0, 1.0);\n" +// " vec4 filtered;\n" +// " mainImage( filtered, texcoord.xy * iChannelResolution[0].xy );\n" +// " vec3 RGB = filtered.rgb * vertexColor.rgb * color.rgb;\n" +// " float maskIntensity = dot(texture(iChannel1, vertexUV).rgb, vec3(1.0/3.0));\n" +// " float A = clamp(filtered.a * vertexColor.a * color.a * maskIntensity, 0.0, 1.0);\n" +// " FragColor = vec4(RGB * A, A);\n" +// "} \n"; -class ImageFilteringShader : public Shader + +std::list< ImageFilter > ImageFilter::presets = { + ImageFilter(), + ImageFilter("Blur", "shaders/filters/blur_1.glsl", "shaders/filters/blur_2.glsl", { { "Amount", 0.5} }), + ImageFilter("HashBlur", "shaders/filters/hashedblur.glsl", "", { { "Radius", 0.5}, { "Iterations", 0.5 } }), + ImageFilter("Unfocus", "shaders/filters/focus.glsl", "", { { "Factor", 0.5} }), + ImageFilter("Denoise", "shaders/filters/denoise.glsl", "", { { "Threshold", 0.5} }), + ImageFilter("Bloom", "shaders/filters/bloom.glsl", "", { { "Intensity", 0.5} }), + ImageFilter("Bokeh", "shaders/filters/bokeh.glsl", "", { { "Radius", 1.0} }), + ImageFilter("Kuwahara", "shaders/filters/kuwahara.glsl", "", { { "Radius", 1.0} }), + ImageFilter("Chalk", "shaders/filters/talk.glsl", "", { { "Factor", 1.0} }), + ImageFilter("Sharpen", "shaders/filters/sharpen.glsl", "", { { "Strength", 1.0} }), + ImageFilter("Sharp Edge", "shaders/filters/sharpenedge.glsl", "", { { "Strength", 0.5} }), + ImageFilter("Freichen", "shaders/filters/freichen.glsl", "", { { "Factor", 0.5} }), + ImageFilter("Sobel", "shaders/filters/sobel.glsl", "", { { "Factor", 0.5} }), + ImageFilter("Pixelate", "shaders/filters/pixelate.glsl", "", { { "Size", 0.5}, { "Edge", 0.5} }), + ImageFilter("Chromakey","shaders/filters/chromakey.glsl", "", { { "Red", 0.05}, { "Green", 0.63}, { "Blue", 0.14}, { "Tolerance", 0.54} }), + ImageFilter("Fisheye", "shaders/filters/fisheye.glsl", "", { { "Factor", 0.5} }), +}; + + + +std::string ImageFilter::getFilterCodeInputs() { + static std::string filterHeaderHelp = "vec3 iResolution; // viewport resolution (in pixels)\n" + "float iTime; // shader playback time (in seconds)\n" + "float iTimeDelta; // render time (in seconds)\n" + "int iFrame; // shader playback frame\n" + "vec3 iChannelResolution[2]; // input channel resolution (in pixels)\n" + "sampler2D iChannel0; // input channel (texture).\n" + "sampler2D iChannel1; // input channel (mask).\n" + "vec4 iDate; // (year, month, day, time in seconds)"; + return filterHeaderHelp; +} + +std::string ImageFilter::getFilterCodeDefault() +{ + return filterDefault; +} + +ImageFilter::ImageFilter() : name_("None"), code_({"shaders/filters/default.glsl",""}) +{ + +} + +ImageFilter::ImageFilter(const std::string &name, const std::string &first_pass, const std::string &second_pass, + const std::map ¶meters) : + name_(name), code_({first_pass, second_pass}), parameters_(parameters) +{ + +} + +ImageFilter::ImageFilter(const ImageFilter &other) : + name_(other.name_), code_(other.code_), parameters_(other.parameters_) +{ + +} + +ImageFilter& ImageFilter::operator= (const ImageFilter& other) +{ + if (this != &other) { + this->name_ = other.name_; + this->code_ = other.code_; + this->parameters_.clear(); + this->parameters_ = other.parameters_; + } + + return *this; +} + +std::pair< std::string, std::string > ImageFilter::code() +{ + // code for filter can be provided by the name of a ressource file + if (Resource::hasPath(code_.first)) + code_.first = Resource::getText(code_.first); + if (Resource::hasPath(code_.second)) + code_.second = Resource::getText(code_.second); + + return code_; +} + +bool ImageFilter::operator!= (const ImageFilter& other) const +{ + if (this->code_.first != other.code_.first) + return true; + if (this->code_.second != other.code_.second) + return true; + + return false; +} + + +class ImageFilteringShader : public ImageShader +{ + // GLSL Program ShadingProgram custom_shading_; // fragment shader GLSL code std::string shader_code_; - - // list of uniform vars in GLSL - // with associated pair (current_value, default_values) in range [0.f 1.f] - std::map uniforms_; + std::string code_; // for iTime & iFrame GTimer *timer_; double iTime_; int iFrame_; + public: + // list of uniforms to control shader + std::map< std::string, float > uniforms_; + ImageFilteringShader(); ~ImageFilteringShader(); void use() override; void reset() override; - void accept(Visitor& v) override; void copy(ImageFilteringShader const& S); - // set fragment shader code and uniforms (with default values) - void setFilterCode(const std::string &code, std::map parameters, std::promise *prom = nullptr); + // set the code of the filter + void setCode(const std::string &code, std::promise *ret = nullptr); + }; -ImageFilteringShader::ImageFilteringShader(): Shader() +ImageFilteringShader::ImageFilteringShader(): ImageShader() { program_ = &custom_shading_; - custom_shading_.setShaders("shaders/image.vs", "shaders/image.fs"); + + shader_code_ = fragmentHeader + filterDefault + fragmentFooter; + custom_shading_.setShaders("shaders/image.vs", shader_code_); timer_ = g_timer_new (); - Shader::reset(); + ImageShader::reset(); } ImageFilteringShader::~ImageFilteringShader() @@ -119,11 +218,12 @@ ImageFilteringShader::~ImageFilteringShader() void ImageFilteringShader::use() { - Shader::use(); + ImageShader::use(); // - // Shadertoy uniforms + // Shader input uniforms // + // Calculate iTime and iTimeDelta double elapsed = g_timer_elapsed (timer_, NULL); program_->setUniform("iTimeDelta", float(elapsed - iTime_) ); @@ -144,155 +244,152 @@ void ImageFilteringShader::use() program_->setUniform("iDate", iDate); // - // loop over all uniforms + // loop over uniforms // for (auto u = uniforms_.begin(); u != uniforms_.end(); ++u) // set uniform to current value - program_->setUniform( u->first, u->second.x ); + program_->setUniform( u->first, u->second ); } void ImageFilteringShader::reset() { - Shader::reset(); + ImageShader::reset(); // reset times g_timer_start(timer_); iFrame_ = 0; - // loop over all uniforms - for (auto u = uniforms_.begin(); u != uniforms_.end(); ++u) - // reset current value to default value - u->second.x = u->second.y; } -void ImageFilteringShader::setFilterCode(const std::string &code, std::map parameters, std::promise *ret) +void ImageFilteringShader::setCode(const std::string &code, std::promise *ret) { - // change the shading code for fragment - shader_code_ = fragmentHeader + code + fragmentFooter; - custom_shading_.setShaders("shaders/image.vs", shader_code_, ret); - - // set the list of uniforms - uniforms_.clear(); - for (auto u = parameters.begin(); u != parameters.end(); ++u) - // set uniform with key name to have default and current values - uniforms_[u->first] = glm::vec2(u->second, u->second); -} - -void ImageFilteringShader::copy(ImageFilteringShader const& S) -{ - // change the shading code for fragment - shader_code_ = S.shader_code_; - custom_shading_.setShaders("shaders/image.vs", shader_code_); - - // set the list of uniforms - uniforms_.clear(); - for (auto u = S.uniforms_.begin(); u != S.uniforms_.end(); ++u) - // set uniform with key name to have default and current values - uniforms_[u->first] = u->second; -} - -void ImageFilteringShader::accept(Visitor& v) -{ -// Shader::accept(v); - v.visit(*this); -} - -std::string ImageFilter::getFilterCodeInputs() -{ - return filterHeaderHelp; -} - -std::string ImageFilter::getFilterCodeDefault() -{ - return filterDefault; -} - -ImageFilter::ImageFilter() : code_(filterDefault) -{ - -} - -ImageFilter::ImageFilter(const ImageFilter &other) : code_(other.code_) -{ - -} - -ImageFilter& ImageFilter::operator = (const ImageFilter& other) -{ - if (this != &other) - this->code_ = other.code_; - - return *this; -} - -bool ImageFilter::operator != (const ImageFilter& other) const -{ - if (this == &other || this->code_ == other.code_) - return false; - - return true; -} - - -// set the code of the filter -void setCode(const std::string &code); - -ImageFilterRenderer::ImageFilterRenderer(glm::vec3 resolution): enabled_(true) -{ - shader_ = new ImageFilteringShader; - surface_ = new Surface(shader_); - buffer_ = new FrameBuffer( resolution, true ); - - std::map< std::string, float > paramfilter; - shader_->setFilterCode( filter_.code(), paramfilter ); -} - -ImageFilterRenderer::~ImageFilterRenderer() -{ - if (buffer_) - delete buffer_; - // NB: shader_ is removed with surface - if (surface_) - delete surface_; -} - -void ImageFilterRenderer::setInputTexture(uint t) -{ - surface_->setTextureIndex( t ); -} - -uint ImageFilterRenderer::getOutputTexture() const -{ - if (enabled_) - return buffer_->texture(); - else - return surface_->textureIndex(); -} - -void ImageFilterRenderer::draw() -{ - if (enabled_) + if (code != code_) { - // render filtered surface into frame buffer - buffer_->begin(); - surface_->draw(glm::identity(), buffer_->projection()); - buffer_->end(); - } -} - -void ImageFilterRenderer::setFilter(const ImageFilter &f, std::promise *ret) -{ - if (f != filter_) { - // set to a different filter : apply change to shader - std::map< std::string, float > paramfilter; - shader_->setFilterCode( f.code(), paramfilter, ret ); + code_ = code; + shader_code_ = fragmentHeader + code_ + fragmentFooter; + custom_shading_.setShaders("shaders/image.vs", shader_code_, ret); } else if (ret != nullptr) { ret->set_value("No change."); } +} - // keep new filter - filter_ = f; +void ImageFilteringShader::copy(ImageFilteringShader const& S) +{ + ImageShader::copy(S); + + // change the shading code for fragment + shader_code_ = S.shader_code_; + custom_shading_.setShaders("shaders/image.vs", shader_code_); } +ImageFilterRenderer::ImageFilterRenderer(glm::vec3 resolution): enabled_(true) +{ + shaders_.first = new ImageFilteringShader; + surfaces_.first = new Surface(shaders_.first); + buffers_.first = new FrameBuffer( resolution, true ); + + shaders_.second = nullptr; + surfaces_.second = nullptr; + buffers_.second = nullptr; +} + +ImageFilterRenderer::~ImageFilterRenderer() +{ + if ( buffers_.first!= nullptr ) + delete buffers_.first; + if ( buffers_.second!= nullptr ) + delete buffers_.second; + + if ( surfaces_.first!= nullptr ) + delete surfaces_.first; + if ( surfaces_.second!= nullptr ) + delete surfaces_.second; + + // NB: shaders_ are removed with surface +} + +void ImageFilterRenderer::setInputTexture(uint t) +{ + surfaces_.first->setTextureIndex( t ); +} + +uint ImageFilterRenderer::getOutputTexture() const +{ + if ( enabled_ ) + return buffers_.second ? buffers_.second->texture() : buffers_.first->texture(); + else + return surfaces_.first->textureIndex(); +} + +void ImageFilterRenderer::draw() +{ + if ( enabled_ ) + { + // render filtered surface into frame buffer + buffers_.first->begin(); + surfaces_.first->draw(glm::identity(), buffers_.first->projection()); + buffers_.first->end(); + + if ( buffers_.second && surfaces_.second ) { + // render filtered surface into frame buffer + buffers_.second->begin(); + surfaces_.second->draw(glm::identity(), buffers_.second->projection()); + buffers_.second->end(); + } + } +} + +ImageFilter ImageFilterRenderer::filter() const +{ + return filter_; +} + +void ImageFilterRenderer::setFilter(const ImageFilter &f, std::promise *ret) +{ + // always keep local copy + filter_ = f; + + // change code + std::pair codes = filter_.code(); + + // set code for first pass + shaders_.first->setCode( codes.first, ret ); + + // no code provided for second pass + if ( codes.second.empty() ) { + + // delete second pass if was previously used + if (buffers_.second!= nullptr ) + delete buffers_.second; + if (surfaces_.second!= nullptr ) + delete surfaces_.second; + + shaders_.second = nullptr; + surfaces_.second = nullptr; + buffers_.second = nullptr; + } + // set code for second pass + else { + // second pass not setup + if (shaders_.second == nullptr) { + + // create shader, surface and buffer for second pass + shaders_.second = new ImageFilteringShader; + surfaces_.second = new Surface(shaders_.second); + surfaces_.second->setTextureIndex( buffers_.first->texture() ); + buffers_.second = new FrameBuffer( buffers_.first->resolution(), true ); + } + + // set the code of the shader for second pass + shaders_.second->setCode( codes.second ); + } + + // change uniforms + shaders_.first->uniforms_ = filter_.parameters(); + if (shaders_.second != nullptr ) + shaders_.second->uniforms_ = filter_.parameters(); + +} + diff --git a/ImageFilter.h b/ImageFilter.h index 0816943..3f5706b 100644 --- a/ImageFilter.h +++ b/ImageFilter.h @@ -2,9 +2,12 @@ #define IMAGEFILTER_H #include +#include #include - #include +#include + +#include "ImageShader.h" class Surface; class FrameBuffer; @@ -13,36 +16,58 @@ class ImageFilteringShader; class ImageFilter { - std::string code_; + std::string name_; + + // code resource file or GLSL (ShaderToy style) + std::pair< std::string, std::string > code_; + + // list of parameters (uniforms names and values) + std::map< std::string, float > parameters_; public: ImageFilter(); + ImageFilter(const std::string &name, const std::string &first_pass, const std::string &second_pass, + const std::map ¶meters); ImageFilter(const ImageFilter &other); - ImageFilter& operator = (const ImageFilter& other); - bool operator !=(const ImageFilter& other) const; + ImageFilter& operator= (const ImageFilter& other); + bool operator!= (const ImageFilter& other) const; + + // get the name of the filter + inline std::string name() const { return name_; } // set the code of the filter - inline void setCode(const std::string &code) { code_ = code; } + inline void setCode(const std::pair< std::string, std::string > &code) { code_ = code; } // get the code of the filter - inline std::string code() const { return code_; } + std::pair< std::string, std::string > code(); - // global + // set the list of parameters of the filter + inline void setParameters(const std::map< std::string, float > ¶meters) { parameters_ = parameters; } + + // get the list of parameters of the filter + inline std::map< std::string, float > parameters() const { return parameters_; } + + // set a value of a parameter + inline void setParameter(const std::string &p, float value) { parameters_[p] = value; } + + // globals static std::string getFilterCodeInputs(); static std::string getFilterCodeDefault(); + static std::list< ImageFilter > presets; }; +class ImageFilteringShader; class ImageFilterRenderer { - Surface *surface_; - FrameBuffer *buffer_; - ImageFilteringShader *shader_; + ImageFilter filter_; bool enabled_; - ImageFilter filter_; + std::pair< Surface *, Surface *> surfaces_; + std::pair< FrameBuffer *, FrameBuffer * > buffers_; + std::pair< ImageFilteringShader *, ImageFilteringShader *> shaders_; public: @@ -62,11 +87,11 @@ public: // get the texture id of the rendered framebuffer uint getOutputTexture() const; - // set the code of the filter + // set the filter void setFilter(const ImageFilter &f, std::promise *ret = nullptr); - // get the code of the filter - inline ImageFilter filter() const { return filter_; } + // get copy of the filter + ImageFilter filter() const; }; diff --git a/Resource.cpp b/Resource.cpp index 7d32081..698e609 100644 --- a/Resource.cpp +++ b/Resource.cpp @@ -339,5 +339,5 @@ std::string Resource::listDirectory() bool Resource::hasPath(const std::string& path) { auto fs = cmrc::vmix::get_filesystem(); - return fs.exists(path); + return !path.empty() && fs.exists(path); } diff --git a/SessionVisitor.cpp b/SessionVisitor.cpp index 30013bc..d05298e 100644 --- a/SessionVisitor.cpp +++ b/SessionVisitor.cpp @@ -41,6 +41,7 @@ using namespace tinyxml2; #include "MultiFileSource.h" #include "ImageShader.h" #include "ImageProcessingShader.h" +#include "ImageFilter.h" #include "MediaPlayer.h" #include "MixingGroup.h" #include "SystemToolkit.h" @@ -508,6 +509,12 @@ void SessionVisitor::visit(ImageProcessingShader &n) XMLElement *chromakey = xmlDoc_->NewElement("chromakey"); chromakey->InsertEndChild( XMLElementFromGLM(xmlDoc_, n.chromakey) ); xmlCurrent_->InsertEndChild(chromakey); +} + + +void SessionVisitor::visit(ImageFilter &f) +{ + } @@ -684,18 +691,27 @@ void SessionVisitor::visit (RenderSource& s) void SessionVisitor::visit (CloneSource& s) { + XMLElement *cloneNode = xmlCurrent_; xmlCurrent_->SetAttribute("type", "CloneSource"); + // origin of clone if (s.origin()) { - xmlCurrent_->SetAttribute("delay", (double) s.delay()); - - XMLElement *origin = xmlDoc_->NewElement("origin"); + XMLElement *origin = xmlDoc_->NewElement( "origin" ); + cloneNode->InsertEndChild(origin); origin->SetAttribute("id", s.origin()->id()); - - xmlCurrent_->InsertEndChild(origin); XMLText *text = xmlDoc_->NewText( s.origin()->name().c_str() ); origin->InsertEndChild( text ); } + + // Delay + xmlCurrent_->SetAttribute( "delay", (double) s.delay()); + + // Filter +// xmlCurrent_ = xmlDoc_->NewElement( "ImageFilter" ); +// cloneNode->InsertEndChild(xmlCurrent_); +//// s.filteringShader()->accept(*this); // TODO save ImageFilter + + xmlCurrent_ = cloneNode; // parent for next visits (other subtypes of Source) } void SessionVisitor::visit (PatternSource& s) diff --git a/SessionVisitor.h b/SessionVisitor.h index 4e02047..102dc4a 100644 --- a/SessionVisitor.h +++ b/SessionVisitor.h @@ -54,6 +54,7 @@ public: void visit (ImageShader& n) override; void visit (MaskShader& n) override; void visit (ImageProcessingShader& n) override; + void visit (ImageFilter& n) override; // Sources void visit (Source& s) override; diff --git a/UserInterfaceManager.cpp b/UserInterfaceManager.cpp index fd58045..c3f7663 100644 --- a/UserInterfaceManager.cpp +++ b/UserInterfaceManager.cpp @@ -1122,11 +1122,10 @@ void UserInterface::showSourceEditor(Source *s) if (s->playable()) { sourcecontrol.setVisible(true); sourcecontrol.resetActiveSelection(); - return; } CloneSource *cs = dynamic_cast(s); if (cs != nullptr) { - Mixer::manager().setCurrentSource( cs->origin() ); + shadercontrol.refresh( cs ); return; } RenderSource *rs = dynamic_cast(s); @@ -5230,12 +5229,13 @@ ShaderEditor::ShaderEditor() : WorkspaceWindow("Shader"), current_(nullptr), { TextEditor::Identifier id; id.mDeclaration = "Shader keyword"; - lang.mIdentifiers.insert(std::make_pair(std::string(k), id)); + lang.mPreprocIdentifiers.insert(std::make_pair(std::string(k), id)); } // init editor _editor.SetLanguageDefinition(lang); _editor.SetHandleKeyboardInputs(true); + _editor.SetShowWhitespaces(false); _editor.SetText(""); // status @@ -5264,6 +5264,14 @@ bool ShaderEditor::Visible() const ); } +void ShaderEditor::refresh(CloneSource *cs) +{ + if ( cs != nullptr && current_ != nullptr && cs == current_ ) { + filters_.erase(current_); + current_ = nullptr; + } +} + void ShaderEditor::Render() { ImGui::SetWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver); @@ -5287,7 +5295,7 @@ void ShaderEditor::Render() if (ImGui::BeginMenu(IMGUI_TITLE_SHADEREDITOR)) { if (ImGui::MenuItem( ICON_FA_SYNC " Reload")) { - if (current_) + if ( current_ != nullptr ) filters_.erase(current_); current_ = nullptr; } @@ -5335,7 +5343,7 @@ void ShaderEditor::Render() if (current_ != nullptr && filters_.find(current_) != filters_.end()) { // set the code of the current filter - filters_[current_].setCode(_editor.GetText()); + filters_[current_].setCode( { _editor.GetText(), "" } ); // change the filter of the current clone // => this triggers compilation of shader @@ -5402,7 +5410,7 @@ void ShaderEditor::Render() c = dynamic_cast(s); if ( c != nullptr ) { // the current source is a clone - if ( filters_.find(c) == filters_.end() ) +// if ( filters_.find(c) == filters_.end() ) // the current clone was not registered filters_[c] = c->filter(); } @@ -5416,7 +5424,7 @@ void ShaderEditor::Render() if ( current_ != c) { // switch to another clone if ( c != nullptr ) { - _editor.SetText(filters_[c].code()); + _editor.SetText( filters_[c].code().first ); _editor.SetReadOnly(false); status_ = "Ready."; } @@ -5426,7 +5434,8 @@ void ShaderEditor::Render() std::string code = _editor.GetText(); code = code.substr(0, code.size() -1); // remember this code as buffered for the filter of this source - filters_[current_].setCode( code ); + filters_[current_].setCode( { code, "" } ); + // cancel editor _editor.SetText(""); _editor.SetReadOnly(true); diff --git a/UserInterfaceManager.h b/UserInterfaceManager.h index 55b958f..2a1430c 100644 --- a/UserInterfaceManager.h +++ b/UserInterfaceManager.h @@ -406,6 +406,8 @@ public: void Render(); void setVisible(bool on); + void refresh(CloneSource *cs); + // from WorkspaceWindow bool Visible() const override; }; diff --git a/Visitor.h b/Visitor.h index 790c37d..00edae2 100644 --- a/Visitor.h +++ b/Visitor.h @@ -26,6 +26,7 @@ class Shader; class ImageShader; class MaskShader; class ImageProcessingShader; +class ImageFilter; class Source; class MediaSource; @@ -78,6 +79,7 @@ public: virtual void visit (ImageShader&) {} virtual void visit (MaskShader&) {} virtual void visit (ImageProcessingShader&) {} + virtual void visit (ImageFilter&) {} // utility virtual void visit (Stream&) {} diff --git a/rsc/shaders/filters/bloom.glsl b/rsc/shaders/filters/bloom.glsl new file mode 100644 index 0000000..94f9dec --- /dev/null +++ b/rsc/shaders/filters/bloom.glsl @@ -0,0 +1,31 @@ +// from https://www.shadertoy.com/view/Ms2Xz3 +uniform float Intensity; + +vec4 BlurColor (in vec2 Coord, in sampler2D Tex, in float MipBias) +{ + vec2 TexelSize = MipBias/iChannelResolution[0].xy; + vec4 Color = texture(Tex, Coord); + Color += texture(Tex, Coord + vec2(TexelSize.x,0.0)); + Color += texture(Tex, Coord + vec2(-TexelSize.x,0.0)); + Color += texture(Tex, Coord + vec2(0.0,TexelSize.y)); + Color += texture(Tex, Coord + vec2(0.0,-TexelSize.y)); + Color += texture(Tex, Coord + vec2(TexelSize.x,TexelSize.y)); + Color += texture(Tex, Coord + vec2(-TexelSize.x,TexelSize.y)); + Color += texture(Tex, Coord + vec2(TexelSize.x,-TexelSize.y)); + Color += texture(Tex, Coord + vec2(-TexelSize.x,-TexelSize.y)); + + return Color/9.0; +} + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + float factor = 0.1 + Intensity * 3.0; + float threshold = 0.8 - (0.6 * Intensity); + + vec2 uv = fragCoord.xy / iResolution.xy; + vec4 Color = texture(iChannel0, uv); + vec4 blurcolor = 0.6 * BlurColor(uv, iChannel0, 1.0) + 0.4 * BlurColor(uv, iChannel0, 3.0); + vec4 Highlight = clamp(blurcolor - threshold, 0.0,1.0) * 1.0/(1.0-threshold); + + fragColor = 1.0 - (1.0-Color) * (1.0 - Highlight * factor); +} diff --git a/rsc/shaders/filters/blur.glsl b/rsc/shaders/filters/blur.glsl new file mode 100644 index 0000000..f04b099 --- /dev/null +++ b/rsc/shaders/filters/blur.glsl @@ -0,0 +1,27 @@ + +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; +} + +void blur( out vec3 rgb, in vec2 fragCoord ) +{ + vec3 tot = vec3(0.0); + + 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)); + + rgb = pow(tot/81.0,vec3(1.0/2.2)); +} + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec3 b; + blur(b, fragCoord); + fragColor = vec4( b, 1.0); +} + + diff --git a/rsc/shaders/filters/blur_1.glsl b/rsc/shaders/filters/blur_1.glsl new file mode 100644 index 0000000..b2e8b52 --- /dev/null +++ b/rsc/shaders/filters/blur_1.glsl @@ -0,0 +1,33 @@ +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 A = vec3(0.0); + vec3 C = vec3(0.0); + float divisor = 0.0; + float weight = 0.0; + float radiusMultiplier = 1.0 / radius; + + for (float y = -radius; y <= radius; y++) + { + A = texture(source, uv + vec2(0.0, y * step)).rgb; + weight = SCurve(1.0 - (abs(y) * radiusMultiplier)); + C += A * weight; + divisor += weight; + } + + return C / divisor; +} + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + float subsampling = 1.0 - ( Amount * 0.9 ); + vec2 uv = fragCoord.xy / ( iResolution.xy * subsampling ); + + // Apply vertical blur on downsampled image (allows to divide radius) + fragColor = vec4( BlurV(iChannel0, uv, 1.0 / (iResolution.y * subsampling), Amount * 100.0 * subsampling), 1.0); +} diff --git a/rsc/shaders/filters/blur_2.glsl b/rsc/shaders/filters/blur_2.glsl new file mode 100644 index 0000000..5e7ee2d --- /dev/null +++ b/rsc/shaders/filters/blur_2.glsl @@ -0,0 +1,34 @@ +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 A = vec3(0.0); + vec3 C = vec3(0.0); + float divisor = 0.0; + float weight = 0.0; + float radiusMultiplier = 1.0 / radius; + + for (float x = -radius; x <= radius; x++) + { + A = texture(source, uv + vec2(x * step, 0.0)).rgb; + weight = SCurve(1.0 - (abs(x) * radiusMultiplier)); + C += A * weight; + divisor += weight; + } + + return C / divisor; +} + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + float subsampling = 1.0 - ( Amount * 0.9 ); + vec2 uv = (fragCoord.xy * subsampling) / iResolution.xy; + + // Apply horizontal blur + fragColor = vec4( BlurH(iChannel0, uv, subsampling / iResolution.x, Amount * 100.0), 1.0); +} diff --git a/rsc/shaders/filters/bokeh.glsl b/rsc/shaders/filters/bokeh.glsl new file mode 100644 index 0000000..7787f2b --- /dev/null +++ b/rsc/shaders/filters/bokeh.glsl @@ -0,0 +1,42 @@ +// from https://www.shadertoy.com/view/4d2Xzw +// Bokeh disc. +// by David Hoskins. +// Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. +// The Golden Angle is (3.-sqrt(5.0))*PI radians +#define GOLDEN_ANGLE 2.39996323 +#define ITERATIONS 60 + +uniform float Radius; + +mat2 rot = mat2(cos(GOLDEN_ANGLE), sin(GOLDEN_ANGLE), -sin(GOLDEN_ANGLE), cos(GOLDEN_ANGLE)); + +vec3 Bokeh(sampler2D tex, vec2 uv, float radius, float amount) +{ + vec3 acc = vec3(0.0); + vec3 div = vec3(0.0); + vec2 pixel = 1.0 / iResolution.xy; + float r = 1.0; + vec2 vangle = vec2(0.0,radius); // Start angle + amount += radius*100.0; + + for (int j = 0; j < ITERATIONS; j++) + { + r += 1. / r; + vangle = rot * vangle; + // (r-1.0) here is the equivalent to sqrt(0, 1, 2, 3...) + vec3 col = texture(tex, uv + pixel * (r-1.) * vangle).xyz; + col = col * col * 1.6; // ...contrast it for better highlights + vec3 bokeh = pow(col, vec3(9.0)) * amount+.4; + acc += col * bokeh; + div += bokeh; + } + return acc / div; +} + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + // Parameters + float a = 40.0; + vec2 uv = fragCoord.xy / iResolution.xy; + fragColor = vec4(Bokeh(iChannel0, uv, Radius, a), 1.0); +} diff --git a/rsc/shaders/filters/chromakey.glsl b/rsc/shaders/filters/chromakey.glsl new file mode 100644 index 0000000..4c11edf --- /dev/null +++ b/rsc/shaders/filters/chromakey.glsl @@ -0,0 +1,58 @@ +/* +chroma key algorithm used to remove the greens screen +this technique is nice as you can choose a color removal range. +using this range, the algorithm will either remove the given color +or blend it with background + +because of this, difficult parts (ie. the sword) can either have a +green-ish look or are a bit transparent (hard to catch with a lot of movement) + +(c) tudordot from https://www.shadertoy.com/view/MlVXWD +*/ + +uniform float Red; +uniform float Green; +uniform float Blue; +uniform float Tolerance; + +//conversion between rgb and YUV +mat4 RGBtoYUV = mat4(0.257, 0.439, -0.148, 0.0, + 0.504, -0.368, -0.291, 0.0, + 0.098, -0.071, 0.439, 0.0, + 0.0625, 0.500, 0.500, 1.0 ); + +//color to be removed +vec4 chromaKey = vec4(Red, Green, Blue, 1); + +//range is used to decide the amount of color to be used from either foreground or background +//if the current distance from pixel color to chromaKey is smaller then maskRange.x we use background, +//if the current distance from pixel color to chromaKey is bigger then maskRange.y we use foreground, +//else, we blend them +//playing with this variable will decide how much the foreground and background blend together +vec2 maskRange = vec2(0.01, Tolerance * 0.5); + +//compute color distance in the UV (CbCr, PbPr) plane +float colorclose(vec3 yuv, vec3 keyYuv, vec2 tol) +{ + float tmp = sqrt(pow(keyYuv.g - yuv.g, 2.0) + pow(keyYuv.b - yuv.b, 2.0)); + if (tmp < tol.x) + return 0.0; + else if (tmp < tol.y) + return (tmp - tol.x)/(tol.y - tol.x); + else + return 1.0; +} + + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec2 fragPos = fragCoord.xy / iResolution.xy; + vec4 texColor0 = texture(iChannel0, fragPos); + + //convert from RGB to YCvCr/YUV + vec4 keyYUV = RGBtoYUV * chromaKey; + vec4 yuv = RGBtoYUV * texColor0; + + float mask = 1.0 - colorclose(yuv.rgb, keyYUV.rgb, maskRange); + fragColor = max(texColor0 - mask * chromaKey, 0.0); +} diff --git a/rsc/shaders/filters/default.glsl b/rsc/shaders/filters/default.glsl new file mode 100644 index 0000000..8fc88c0 --- /dev/null +++ b/rsc/shaders/filters/default.glsl @@ -0,0 +1,5 @@ +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec2 uv = fragCoord.xy / iResolution.xy; + fragColor = texture(iChannel0, uv); +} diff --git a/rsc/shaders/filters/denoise.glsl b/rsc/shaders/filters/denoise.glsl new file mode 100644 index 0000000..bdcccb4 --- /dev/null +++ b/rsc/shaders/filters/denoise.glsl @@ -0,0 +1,141 @@ +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Copyright (c) 2018-2019 Michele Morrone +// All rights reserved. +// +// https://michelemorrone.eu - https://BrutPitt.com +// +// me@michelemorrone.eu - brutpitt@gmail.com +// twitter: @BrutPitt - github: BrutPitt +// +// https://github.com/BrutPitt/glslSmartDeNoise/ +// +// This software is distributed under the terms of the BSD 2-Clause license +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +uniform float Threshold; + +#define INV_SQRT_OF_2PI 0.39894228040143267793994605993439 // 1.0/SQRT_OF_2PI +#define INV_PI 0.31830988618379067153776752674503 + +// smartDeNoise - parameters +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// sampler2D tex - sampler image / texture +// vec2 uv - actual fragment coord +// float sigma > 0 - sigma Standard Deviation +// float kSigma >= 0 - sigma coefficient +// kSigma * sigma --> radius of the circular kernel +// float threshold - edge sharpening threshold + +vec4 smartDeNoise(sampler2D tex, vec2 uv, float sigma, float kSigma, float threshold) +{ + float radius = round(kSigma*sigma); + float radQ = radius * radius; + + float invSigmaQx2 = .5 / (sigma * sigma); // 1.0 / (sigma^2 * 2.0) + float invSigmaQx2PI = INV_PI * invSigmaQx2; // 1.0 / (sqrt(PI) * sigma) + + float invThresholdSqx2 = .5 / (threshold * threshold); // 1.0 / (sigma^2 * 2.0) + float invThresholdSqrt2PI = INV_SQRT_OF_2PI / threshold; // 1.0 / (sqrt(2*PI) * sigma) + + vec4 centrPx = texture(tex,uv); + + float zBuff = 0.0; + vec4 aBuff = vec4(0.0); + vec2 size = vec2(textureSize(tex, 0)); + + for(float x=-radius; x <= radius; x++) { + float pt = sqrt(radQ-x*x); // pt = yRadius: have circular trend + for(float y=-pt; y <= pt; y++) { + vec2 d = vec2(x,y); + + float blurFactor = exp( -dot(d , d) * invSigmaQx2 ) * invSigmaQx2PI; + + vec4 walkPx = texture(tex,uv+d/size); + + vec4 dC = walkPx-centrPx; + float deltaFactor = exp( -dot(dC, dC) * invThresholdSqx2) * invThresholdSqrt2PI * blurFactor; + + zBuff += deltaFactor; + aBuff += deltaFactor*walkPx; + } + } + return aBuff/zBuff; +} + +// About Standard Deviations (watch Gauss curve) +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// kSigma = 1*sigma cover 68% of data +// kSigma = 2*sigma cover 95% of data - but there are over 3 times +// more points to compute +// kSigma = 3*sigma cover 99.7% of data - but needs more than double +// the calculations of 2*sigma + + +// Optimizations (description) +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// fX = exp( -(x*x) * invSigmaSqx2 ) * invSigmaxSqrt2PI; +// fY = exp( -(y*y) * invSigmaSqx2 ) * invSigmaxSqrt2PI; +// where... +// invSigmaSqx2 = 1.0 / (sigma^2 * 2.0) +// invSigmaxSqrt2PI = 1.0 / (sqrt(2 * PI) * sigma) +// +// now, fX*fY can be written in unique expression... +// +// e^(a*X) * e^(a*Y) * c*c +// +// where: +// a = invSigmaSqx2, X = (x*x), Y = (y*y), c = invSigmaxSqrt2PI +// +// -[(x*x) * 1/(2 * sigma^2)] -[(y*y) * 1/(2 * sigma^2)] +// e e +// fX = ------------------------------- fY = ------------------------------- +// ________ ________ +// \/ 2 * PI * sigma \/ 2 * PI * sigma +// +// now with... +// a = 1/(2 * sigma^2), +// X = (x*x) +// Y = (y*y) ________ +// c = 1 / \/ 2 * PI * sigma +// +// we have... +// -[aX] -[aY] +// fX = e * c; fY = e * c; +// +// and... +// -[aX + aY] [2] -[a(X + Y)] [2] +// fX*fY = e * c = e * c +// +// well... +// +// -[(x*x + y*y) * 1/(2 * sigma^2)] +// e +// fX*fY = -------------------------------------- +// [2] +// 2 * PI * sigma +// +// now with assigned constants... +// +// invSigmaQx2 = 1/(2 * sigma^2) +// invSigmaQx2PI = 1/(2 * PI * sigma^2) = invSigmaQx2 * INV_PI +// +// and the kernel vector +// +// k = vec2(x,y) +// +// we can write: +// +// fXY = exp( -dot(k,k) * invSigmaQx2) * invSigmaQx2PI +// + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + // Normalized pixel coordinates (from 0 to 1) + vec2 uv = fragCoord/iResolution.xy; + fragColor = smartDeNoise(iChannel0, uv, 5.0, 2.0, mix(0.01, 0.1, Threshold)); + +} + diff --git a/rsc/shaders/filters/edge.glsl b/rsc/shaders/filters/edge.glsl new file mode 100644 index 0000000..31320c7 --- /dev/null +++ b/rsc/shaders/filters/edge.glsl @@ -0,0 +1,22 @@ +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec3 c[16]; + for (int i=0; i < 4; ++i) + for (int j=0; j < 4; ++j) + { + c[4*i+j] = texture(iChannel0, (fragCoord.xy+vec2(i-2,j-2)) / iResolution.xy).rgb; + } + + vec3 Lx = 2.0*(c[14]-c[2]) + 2.0*(c[13]-c[1]) + c[15] - c[3] + c[12] - c[0]; + Lx += 0.5 * (2.0*(c[10]-c[6]) + 2.0*(c[9]-c[5]) + c[11] - c[7] + c[8] - c[4] ); + + vec3 Ly = 2.0*(c[8]-c[11]) + 2.0*(c[4]-c[7]) + c[12] + c[0] - c[15] - c[3]; + Ly += 0.5 * (2.0*(c[9]-c[10]) + 2.0*(c[5]-c[6]) + c[13] + c[1] - c[14] - c[2] ); + + //vec3 G = sqrt(Lx*Lx+Ly*Ly); + vec3 G = abs(Lx)+abs(Ly); + + fragColor = vec4( mix(vec3(1.0), vec3(0.0), pow(length(G),2) ), 1.0); + +} + diff --git a/rsc/shaders/filters/fisheye.glsl b/rsc/shaders/filters/fisheye.glsl new file mode 100644 index 0000000..271a30b --- /dev/null +++ b/rsc/shaders/filters/fisheye.glsl @@ -0,0 +1,31 @@ +// from https://www.shadertoy.com/view/4s2GRR +// Inspired by http://stackoverflow.com/questions/6030814/add-fisheye-effect-to-images-at-runtime-using-opengl-es +uniform float Factor; + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec2 p = fragCoord.xy / iResolution.x; + float prop = iResolution.x / iResolution.y; //screen proportion + vec2 m = vec2(0.5, 0.5 / prop); //center coords + vec2 d = p - m; //vector from center to current fragment + float r = sqrt(dot(d, d)); // distance of pixel from center + + // amount of effect + float power = ( 2.0 * 3.141592 / (2.0 * sqrt(dot(m, m))) ) * (Factor-0.5); + + float bind; //radius of 1:1 effect + if (power > 0.0) bind = sqrt(dot(m, m)); //stick to corners + else {if (prop < 1.0) bind = m.x; else bind = m.y;} //stick to borders + + //Weird formulas + vec2 uv; + if (power > 0.0)//fisheye + uv = m + normalize(d) * tan(r * power) * bind / tan( bind * power); + else if (power < 0.0)//antifisheye + uv = m + normalize(d) * atan(r * -power * 10.0) * bind / atan(-power * bind * 10.0); + else uv = p;//no effect for power = 1.0 + + // Second part of cheat for round effect, not elliptical + vec3 col = texture(iChannel0, vec2(uv.x, uv.y * prop)).xyz; + fragColor = vec4(col, 1.0); +} diff --git a/rsc/shaders/filters/focus.glsl b/rsc/shaders/filters/focus.glsl new file mode 100644 index 0000000..a54aff2 --- /dev/null +++ b/rsc/shaders/filters/focus.glsl @@ -0,0 +1,52 @@ +// Bilateral filter +// (c) mrharicot https://www.shadertoy.com/view/4dfGDH + +#define SIGMA 10.0 +#define MSIZE 13 + +uniform float Factor; + +float normpdf(in float x, in float sigma) +{ + return 0.39894*exp(-0.5*x*x/(sigma*sigma))/sigma; +} + +float normpdf3(in vec3 v, in float sigma) +{ + return 0.39894*exp(-0.5*dot(v,v)/(sigma*sigma))/sigma; +} + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec3 c = texture(iChannel0, (fragCoord.xy / iResolution.xy) ).rgb; + + const int kSize = (MSIZE-1)/2; + float kernel[MSIZE]; + vec3 final_colour = vec3(0.0); + + //create the 1-D kernel + float Z = 0.0; + for (int j = 0; j <= kSize; ++j) + { + kernel[kSize+j] = kernel[kSize-j] = normpdf(float(j), SIGMA); + } + + vec3 cc; + float fac; + float bsigma = mix(0.01, 1.2, Factor); + float bZ = 1.0/normpdf(0.0, bsigma); + for (int i=-kSize; i <= kSize; ++i) + { + for (int j=-kSize; j <= kSize; ++j) + { + cc = texture(iChannel0, (fragCoord.xy+vec2(float(i),float(j))) / iResolution.xy).rgb; + fac = normpdf3(cc-c, bsigma)*bZ*kernel[kSize+j]*kernel[kSize+i]; + Z += fac; + final_colour += fac*cc; + + } + } + + fragColor = vec4(final_colour/Z, 1.0); +} + diff --git a/rsc/shaders/filters/freichen.glsl b/rsc/shaders/filters/freichen.glsl new file mode 100644 index 0000000..9c4074c --- /dev/null +++ b/rsc/shaders/filters/freichen.glsl @@ -0,0 +1,44 @@ +uniform float Factor; + +const mat3 G[9] = mat3[]( + mat3( 0.3535533845424652, 0, -0.3535533845424652, 0.5, 0, -0.5, 0.3535533845424652, 0, -0.3535533845424652 ), + mat3( 0.3535533845424652, 0.5, 0.3535533845424652, 0, 0, 0, -0.3535533845424652, -0.5, -0.3535533845424652 ), + mat3( 0, 0.3535533845424652, -0.5, -0.3535533845424652, 0, 0.3535533845424652, 0.5, -0.3535533845424652, 0 ), + mat3( 0.5, -0.3535533845424652, 0, -0.3535533845424652, 0, 0.3535533845424652, 0, 0.3535533845424652, -0.5 ), + mat3( 0, -0.5, 0, 0.5, 0, 0.5, 0, -0.5, 0 ), + mat3( -0.5, 0, 0.5, 0, 0, 0, 0.5, 0, -0.5 ), + mat3( 0.1666666716337204, -0.3333333432674408, 0.1666666716337204, -0.3333333432674408, 0.6666666865348816, -0.3333333432674408, 0.1666666716337204, -0.3333333432674408, 0.1666666716337204 ), + mat3( -0.3333333432674408, 0.1666666716337204, -0.3333333432674408, 0.1666666716337204, 0.6666666865348816, 0.1666666716337204, -0.3333333432674408, 0.1666666716337204, -0.3333333432674408 ), + mat3( 0.3333333432674408, 0.3333333432674408, 0.3333333432674408, 0.3333333432674408, 0.3333333432674408, 0.3333333432674408, 0.3333333432674408, 0.3333333432674408, 0.3333333432674408 ) +); + +void freichen( out vec3 rgb, in vec2 fragCoord ) +{ + mat3 I; + float cnv[9]; + vec3 sample; + for (int i=0; i<3; i++) + for (int j=0; j<3; j++) { + sample = texture(iChannel0, (fragCoord + vec2(i-1,j-1)) / iResolution.xy ).rgb; + I[i][j] = length(sample); + } + for (int i=0; i<9; i++) { + float dp3 = dot(G[i][0], I[0]) + dot(G[i][1], I[1]) + dot(G[i][2], I[2]); + cnv[i] = dp3 * dp3; + } + float M = cnv[0] + cnv[1] + cnv[2] + cnv[3]; + float S = M + cnv[4] + cnv[5] + cnv[6] + cnv[7] + cnv[8]; + + rgb = vec3( (1.0 + 10.0 * Factor) * sqrt(M/S) ); +} + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec3 v; + freichen(v, fragCoord); + fragColor = vec4( vec3(1.0) - v, 1.0); +} + + + + diff --git a/rsc/shaders/filters/hashedblur.glsl b/rsc/shaders/filters/hashedblur.glsl new file mode 100644 index 0000000..d94ec64 --- /dev/null +++ b/rsc/shaders/filters/hashedblur.glsl @@ -0,0 +1,52 @@ +// Hashed blur +// David Hoskins. +// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. +// https://www.shadertoy.com/view/XdjSRw + +// Set this to iTime to add pixel noise. +#define TIME 0.0 + +uniform float Radius; +uniform float Iterations; + +// hash function generate random radius and angle... +#define TAU 6.28318530718 +vec2 Sample(inout vec2 r) +{ + r = fract(r * vec2(33.3983, 43.4427)); + return sqrt(r.x+.001) * vec2(sin(r.y * TAU), cos(r.y * TAU))*.5; // circular sampling. +} + +#define HASHSCALE 443.8975 +vec2 Hash22(vec2 p) +{ + vec3 p3 = fract(vec3(p.xyx) * HASHSCALE); + p3 += dot(p3, p3.yzx+19.19); + return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y)); +} + +vec3 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 + for (int i = 0; i < max; i++) + { + acc += texture(iChannel0, uv + circle * Sample(random), radius*10.0).xyz; + } + return acc / float(max); +} + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec2 uv = fragCoord.xy / iResolution.xy; + + float radius = Radius * 0.005 * iResolution.y; + radius = pow(radius, 2.0); + + fragColor = vec4(Blur(uv * vec2(1.0, -1.0), radius), 1.0); +} diff --git a/rsc/shaders/filters/kuwahara.glsl b/rsc/shaders/filters/kuwahara.glsl new file mode 100644 index 0000000..c6acc7f --- /dev/null +++ b/rsc/shaders/filters/kuwahara.glsl @@ -0,0 +1,100 @@ +////////// Original code credits: ////////// +// by Jan Eric Kyprianidis +// https://code.google.com/p/gpuakf/source/browse/glsl/kuwahara.glsl +//////////////////////////////////////////// +// This shader is modified to be compatible with LOVE2D's quirky shader engine! +// Modified by Christian D. Madsen +// https://github.com/strangewarp/LoveAnisotropicKuwahara + +uniform float Radius; + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec4 outpix; + + int rad = int(Radius * 10.0); + + vec2 src_size = iResolution.xy; + vec2 uv = fragCoord.xy / iResolution.xy; + float n = float((rad + 1) * (rad + 1)); + + //////////////////////////////////////////////////////////////////////////////// + // Bilateral difference-check, to speed up the processing of large flat areas // + //////////////////////////////////////////////////////////////////////////////// + vec3 compare = texture(iChannel0, uv / src_size).rgb; + bool diffpix = false; + for (int j = -rad; j <= rad; ++j) { + if (texture(iChannel0, uv + vec2(0,j) / src_size).rgb != compare) { + diffpix = true; + break; + } + } + if (!diffpix) { + for (int i = -rad; i <= rad; ++i) { + if (texture(iChannel0, uv + vec2(i,0) / src_size).rgb != compare) { + diffpix = true; + break; + } + } + } + if (!diffpix) { + outpix = vec4( texture(iChannel0, uv / src_size).rgb, 1.0); + } + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + else { + vec3 m[4]; + vec3 s[4]; + for (int k = 0; k < 4; ++k) { + m[k] = vec3(0.0); + s[k] = vec3(0.0); + } + + for (int j = -rad; j <= 0; ++j) { + for (int i = -rad; i <= 0; ++i) { + vec3 c = texture(iChannel0, uv + vec2(i,j) / src_size).rgb; + m[0] += c; + s[0] += c * c; + } + } + + for (int j = -rad; j <= 0; ++j) { + for (int i = 0; i <= rad; ++i) { + vec3 c = texture(iChannel0, uv + vec2(i,j) / src_size).rgb; + m[1] += c; + s[1] += c * c; + } + } + + for (int j = 0; j <= rad; ++j) { + for (int i = 0; i <= rad; ++i) { + vec3 c = texture(iChannel0, uv + vec2(i,j) / src_size).rgb; + m[2] += c; + s[2] += c * c; + } + } + + for (int j = 0; j <= rad; ++j) { + for (int i = -rad; i <= 0; ++i) { + vec3 c = texture(iChannel0, uv + vec2(i,j) / src_size).rgb; + m[3] += c; + s[3] += c * c; + } + } + + float min_sigma2 = 1e+2; + for (int k = 0; k < 4; ++k) { + m[k] /= n; + s[k] = abs(s[k] / n - m[k] * m[k]); + float sigma2 = s[k].r + s[k].g + s[k].b; + if (sigma2 < min_sigma2) { + min_sigma2 = sigma2; + outpix = vec4(m[k], 1.0); + } + } + } + + fragColor = outpix; +} + + diff --git a/rsc/shaders/filters/pixelate.glsl b/rsc/shaders/filters/pixelate.glsl new file mode 100644 index 0000000..69b5523 --- /dev/null +++ b/rsc/shaders/filters/pixelate.glsl @@ -0,0 +1,24 @@ +uniform float Size; +uniform float Edge; + +const mat3 I = mat3( 0, 0, 0, 0, 1, 0, 0, 0, 0); +const mat3 G = mat3( 0.0625, 0.125, 0.0625, 0.125, 0.25, 0.125, 0.0625, 0.125, 0.0625); + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) { + + vec2 size = vec2(3.0 * iResolution.x / iResolution.y, 3.0 ); + size = mix( size, iResolution.xy, pow(1.0 - Size, 3) ) ; + vec2 vUv = size / iResolution.xy; + + vec4 blur = vec4(0,0,0,0); + for (int i=0; i<3; i++) + for (int j=0; j<3; j++) { + blur += G[i][j] * texture(iChannel0, floor(vUv * (fragCoord + vec2(i-1,j-1))) / size ); + } + + vec4 sample = texture(iChannel0, floor(vUv * fragCoord) / size ); + sample += Edge * (sample - blur); + + fragColor = sample; +} + diff --git a/rsc/shaders/filters/sharpen.glsl b/rsc/shaders/filters/sharpen.glsl new file mode 100644 index 0000000..d9af99b --- /dev/null +++ b/rsc/shaders/filters/sharpen.glsl @@ -0,0 +1,50 @@ +uniform float Strength; + +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; +} + +void blur( out vec3 rgb, in vec2 fragCoord ) +{ + vec3 tot = vec3(0.0); + + 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)); + + rgb = pow(tot/81.0,vec3(1.0/2.2)); +} + +void sharpen( out vec3 rgb, in vec2 fragCoord ) +{ + rgb = + texSample(-1,-1, fragCoord) * -1. + + texSample( 0,-1, fragCoord) * -1. + + texSample( 1,-1, fragCoord) * -1. + + texSample(-1, 0, fragCoord) * -1. + + texSample( 0, 0, fragCoord) * 9. + + texSample( 1, 0, fragCoord) * -1. + + texSample(-1, 1, fragCoord) * -1. + + texSample( 0, 1, fragCoord) * -1. + + texSample( 1, 1, fragCoord) * -1. ; +} + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec3 original = texSample( 0, 0, fragCoord); + // sharpen mask + vec3 sharped; + sharpen(sharped, fragCoord); + // sharpen by subtracting blurred image to original + vec3 blurred; + blur(blurred, fragCoord); + blurred = original + (original-blurred); + // progressive mix from original to most sharpen + sharped = mix(sharped, blurred, Strength); + fragColor = vec4( mix(original, sharped, Strength * 2.0), 1.0); +} + + diff --git a/rsc/shaders/filters/sharpenedge.glsl b/rsc/shaders/filters/sharpenedge.glsl new file mode 100644 index 0000000..146f34c --- /dev/null +++ b/rsc/shaders/filters/sharpenedge.glsl @@ -0,0 +1,43 @@ +uniform float Strength; + +vec3 blurSample(in vec2 uv, in vec2 xoff, in vec2 yoff) +{ + vec3 v11 = texture(iChannel0, uv + xoff).rgb; + vec3 v12 = texture(iChannel0, uv + yoff).rgb; + vec3 v21 = texture(iChannel0, uv - xoff).rgb; + vec3 v22 = texture(iChannel0, uv - yoff).rgb; + return (v11 + v12 + v21 + v22 + 2.0 * texture(iChannel0, uv).rgb) * 0.166667; +} + +vec3 edgeStrength(in vec2 uv) +{ + const float spread = 0.5; + vec2 offset = vec2(1.0) / iChannelResolution[0].xy; + vec2 up = vec2(0.0, offset.y) * spread; + vec2 right = vec2(offset.x, 0.0) * spread; + const float frad = 3.0; + vec3 v11 = blurSample(uv + up - right, right, up); + vec3 v12 = blurSample(uv + up, right, up); + vec3 v13 = blurSample(uv + up + right, right, up); + + vec3 v21 = blurSample(uv - right, right, up); + vec3 v22 = blurSample(uv, right, up); + vec3 v23 = blurSample(uv + right, right, up); + + vec3 v31 = blurSample(uv - up - right, right, up); + vec3 v32 = blurSample(uv - up, right, up); + vec3 v33 = blurSample(uv - up + right, right, up); + + vec3 laplacian_of_g = v11 * 0.0 + v12 * 1.0 + v13 * 0.0 + + v21 * 1.0 + v22 * -4.0 + v23 * 1.0 + + v31 * 0.0 + v32 * 1.0 + v33 * 0.0; + laplacian_of_g = laplacian_of_g * 1.0; + return laplacian_of_g.xyz; +} + + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec2 uv = fragCoord.xy / iResolution.xy; + fragColor = vec4(texture(iChannel0, uv).xyz - edgeStrength(uv) * Strength * (iResolution.y*0.05), 1.0); +} diff --git a/rsc/shaders/filters/sketch.glsl b/rsc/shaders/filters/sketch.glsl new file mode 100644 index 0000000..61901ab --- /dev/null +++ b/rsc/shaders/filters/sketch.glsl @@ -0,0 +1,90 @@ + +/** + * Sketchy Stippling / Dot-Drawing Effect by Ruofei Du (DuRuofei.com) + * Link to demo: https://www.shadertoy.com/view/ldSyzV + * starea @ ShaderToy, License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. + * + * A one-pass shader for dotted drawing / sketch post processing effect. + * Press the mouse for a slower but more classic sketching effect, though I prefer the dotted version. + * Shader forked and related ones are listed below. + * Works better with video mipmaping. + * + * Reference: + * [1] Pencil vs Camera. http://www.duruofei.com/Research/pencilvscamera + * + * Forked or related: + * [1] Pol's Photoshop Blends Branchless: https://www.shadertoy.com/view/Md3GzX + * [2] Gaussian Blur: https://www.shadertoy.com/view/ltBXRh + * [3] williammalo2's Blur with only one pixel read: https://www.shadertoy.com/view/XtGGzz + * [3] demofox's greyscale: https://www.shadertoy.com/view/XdXSzX + * [4] iq's Postprocessing: https://www.shadertoy.com/view/4dfGzn + * [5] related blur: https://www.shadertoy.com/view/XsVBDR + * + * Related masterpieces: + * [1] flockaroo's Notebook Drawings: https://www.shadertoy.com/view/XtVGD1 + * [2] HLorenzi's Hand-drawn sketch: https://www.shadertoy.com/view/MsSGD1 + **/ +const float PI = 3.1415926536; +const float PI2 = PI * 2.0; +const int mSize = 9; +const int kSize = (mSize-1)/2; +const float sigma = 3.0; +float kernel[mSize]; + +// Gaussian PDF +float normpdf(in float x, in float sigma) +{ + return 0.39894 * exp(-0.5 * x * x / (sigma * sigma)) / sigma; +} + +// +vec3 colorDodge(in vec3 src, in vec3 dst) +{ + return step(0.0, dst) * mix(min(vec3(1.0), dst/ (1.0 - src)), vec3(1.0), step(1.0, src)); +} + +float greyScale(in vec3 col) +{ + return dot(col, vec3(0.3, 0.59, 0.11)); + //return dot(col, vec3(0.2126, 0.7152, 0.0722)); //sRGB +} + +vec2 random(vec2 p){ + p = fract(p * (vec2(314.159, 314.265))); + p += dot(p, p.yx + 17.17); + return fract((p.xx + p.yx) * p.xy); +} + +#define HASHSCALE 443.8975 +vec2 random2(vec2 p) +{ + vec3 p3 = fract(vec3(p.xyx) * HASHSCALE); + p3 += dot(p3, p3.yzx+19.19); + return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y)); +} + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec2 q = fragCoord.xy / iResolution.xy; + vec3 col = texture(iChannel0, q).rgb; + + vec2 r = random(q); + r.x *= PI2; + vec2 cr = vec2(sin(r.x),cos(r.x))*sqrt(r.y); + + vec3 blurred = texture(iChannel0, q + cr * (vec2(mSize) / iResolution.xy) ).rgb; + + vec3 inv = vec3(1.0) - blurred; + // color dodge + vec3 lighten = colorDodge(col, inv); + // grey scale + vec3 res = vec3(greyScale(lighten)); + + // more contrast + res = vec3(pow(res.x, 3.0)); + //res = clamp(res * 0.7 + 0.3 * res * res * 1.2, 0.0, 1.0); + + fragColor = vec4(res, 1.0); +} + + diff --git a/rsc/shaders/filters/sobel.glsl b/rsc/shaders/filters/sobel.glsl new file mode 100644 index 0000000..4f1c49a --- /dev/null +++ b/rsc/shaders/filters/sobel.glsl @@ -0,0 +1,36 @@ +uniform float Factor; + +const mat3 G[2] = mat3[]( + mat3( 3.0, 10.0, 3.0, 0.0, 0.0, 0.0, -3.0, -10.0, -3.0 ), + mat3( 3.0, 0.0, -3.0, 10.0, 0.0, -10.0, 3.0, 0.0, -3.0 ) +); + +void sobel( out vec3 rgb, in vec2 fragCoord ) +{ + mat3 I; + float cnv[2]; + vec3 sample; + for (int i=0; i<3; i++) + for (int j=0; j<3; j++) { + sample = texture(iChannel0, (fragCoord + vec2(i-1,j-1)) / iResolution.xy ).rgb; + I[i][j] = length(sample); + } + for (int i=0; i<2; i++) { + float dp3 = dot(G[i][0], I[0]) + dot(G[i][1], I[1]) + dot(G[i][2], I[2]); + cnv[i] = dp3 * dp3; + } + // rgb = vec3( (0.1 + 4.0 * Factor) * sqrt(cnv[0]*cnv[0]+cnv[1]*cnv[1]) ); + + rgb = vec3( step( Factor, 2.0 * ( abs(cnv[0]) + abs(cnv[1]) ) ) ); +} + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec3 v; + sobel(v, fragCoord); + fragColor = vec4( vec3(1.0) - v, 1.0); +} + + + + diff --git a/rsc/shaders/filters/talk.glsl b/rsc/shaders/filters/talk.glsl new file mode 100644 index 0000000..e5b37a8 --- /dev/null +++ b/rsc/shaders/filters/talk.glsl @@ -0,0 +1,46 @@ +// From https://www.shadertoy.com/view/XdfcWN +// Created by inigo quilez - iq/2014 +// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. +uniform float Factor; + +vec2 hash( vec2 p ) { + p = 2.0 * vec2(dot(p,vec2(127.1,311.7)), dot(p,vec2(269.5,183.3))); + return -1.0 + 2.0*fract(sin(p)*43758.5453123); +} + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec2 uv = fragCoord.xy / iResolution.xy; + + // sample zbuffer (in linear eye space) at the current shading point + float zr = 1.0-texture( iChannel0, fragCoord.xy / iResolution.xy ).x; + + // sample neighbor pixels + float ao = 0.0; + for( int i=0; i<8; i++ ) + { + // get a random 2D offset vector + vec2 off = -1.0 + 2.0 * hash( fragCoord.xy) *float(i); + // sample the zbuffer at a neightbor pixel (in a 16 pixel radious) + float z = 1.0-texture( iChannel0, (fragCoord.xy + floor(off*16.0))/iResolution.xy ).x; + // accumulate occlusion if difference is less than 0.1 units + ao += clamp( (zr-z)/0.1, 0.0, 1.0); + } + // average down the occlusion + ao = clamp( 1.0 - ao/8.0, 0.0, 1.0 ); + + vec3 col = vec3(ao); + vec3 og = texture(iChannel0, uv).xyz; + + vec3 one = vec3(1.); + vec3 two = vec3(2.); + vec3 point5 = vec3(0.5); + col = col.x > 0.5 ? one - (one - og) * (one - two * (col - point5)) : col * two * og; + + // hard light + col.r = col.r > 0.5 ? 1. - (1. - og.r) * (1. - 2. * (col.r - 0.5)) : col.r * 2. * og.r; + col.g = col.g > 0.5 ? 1. - (1. - og.g) * (1. - 2. * (col.g - 0.5)) : col.g * 2. * og.g; + col.b = col.b > 0.5 ? 1. - (1. - og.b) * (1. - 2. * (col.b - 0.5)) : col.b * 2. * og.b; + + fragColor = vec4( mix(texture(iChannel0, uv).xyz, col, Factor), 1.0); +} diff --git a/rsc/shaders/image.fs b/rsc/shaders/image.fs index 1de3ae1..ee8831c 100644 --- a/rsc/shaders/image.fs +++ b/rsc/shaders/image.fs @@ -20,7 +20,7 @@ void main() // adjust UV vec4 texcoord = iTransform * vec4(vertexUV.x, vertexUV.y, 0.0, 1.0); - // color is a mix of texture (manipulated with brightness & contrast), vertex and uniform colors + // color is a mix of texture, vertex and uniform colors vec4 textureColor = texture(iChannel0, texcoord.xy); vec3 RGB = textureColor.rgb * vertexColor.rgb * color.rgb;