From 551acf25b986e8fafb0be8f583b2ec188ff4229a Mon Sep 17 00:00:00 2001 From: brunoherbelin Date: Sat, 25 Apr 2020 23:58:49 +0200 Subject: [PATCH] New Shader Image Processing Cleanup shaders and add image processing shader for source --- CMakeLists.txt | 10 +- ImGuiVisitor.cpp | 68 ++- ImGuiVisitor.h | 7 +- ImageProcessingShader.cpp | 48 +- ImageProcessingShader.h | 32 +- ImageShader.cpp | 2 +- PickingVisitor.h | 5 - SearchVisitor.h | 4 - SessionVisitor.cpp | 36 +- SessionVisitor.h | 1 + Shader.cpp | 8 +- Shader.h | 2 +- Source.cpp | 23 +- Source.h | 13 +- Visitor.h | 9 +- main.cpp | 9 +- rsc/images/shadow.png | Bin 0 -> 5314 bytes rsc/images/shadow_dark.png | Bin 0 -> 6383 bytes rsc/mesh/circle.ply | 462 ++++++++++++++++++++ rsc/shaders/{texture-shader.fs => image.fs} | 7 +- rsc/shaders/{texture-shader.vs => image.vs} | 0 rsc/shaders/imageprocessing.fs | 326 ++++++++++++++ rsc/shaders/{simple-shader.fs => simple.fs} | 2 +- rsc/shaders/{simple-shader.vs => simple.vs} | 0 24 files changed, 1009 insertions(+), 65 deletions(-) create mode 100755 rsc/images/shadow.png create mode 100755 rsc/images/shadow_dark.png create mode 100644 rsc/mesh/circle.ply rename rsc/shaders/{texture-shader.fs => image.fs} (77%) rename rsc/shaders/{texture-shader.vs => image.vs} (100%) create mode 100644 rsc/shaders/imageprocessing.fs rename rsc/shaders/{simple-shader.fs => simple.fs} (71%) rename rsc/shaders/{simple-shader.vs => simple.vs} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 62a46fe..278f7f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -202,6 +202,7 @@ set(VMIX_SRCS Log.cpp Shader.cpp ImageShader.cpp + ImageProcessingShader.cpp Scene.cpp Primitives.cpp Mesh.cpp @@ -226,10 +227,11 @@ set(VMIX_SRCS ) set(VMIX_RSC_FILES - ./rsc/shaders/simple-shader.fs - ./rsc/shaders/simple-shader.vs - ./rsc/shaders/texture-shader.fs - ./rsc/shaders/texture-shader.vs + ./rsc/shaders/simple.fs + ./rsc/shaders/simple.vs + ./rsc/shaders/image.fs + ./rsc/shaders/image.vs + ./rsc/shaders/imageprocessing.fs ./rsc/fonts/Hack-Regular.ttf ./rsc/fonts/Roboto-Regular.ttf ./rsc/fonts/Roboto-Bold.ttf diff --git a/ImGuiVisitor.cpp b/ImGuiVisitor.cpp index d4ba684..4d1d53a 100644 --- a/ImGuiVisitor.cpp +++ b/ImGuiVisitor.cpp @@ -8,6 +8,7 @@ #include "Scene.h" #include "Primitives.h" #include "ImageShader.h" +#include "ImageProcessingShader.h" #include "MediaPlayer.h" #include "imgui.h" @@ -111,7 +112,11 @@ void ImGuiVisitor::visit(Shader &n) { ImGui::PushID(n.id()); - ImGui::ColorEdit3("color", glm::value_ptr(n.color) ) ; + if (ImGuiToolkit::ButtonIcon(14, 8)) { + n.color = glm::vec4(1.f, 1.f, 1.f, 1.f); + } + ImGui::SameLine(0, 10); + ImGui::ColorEdit3("Color", glm::value_ptr(n.color) ) ; ImGui::PopID(); } @@ -120,13 +125,13 @@ void ImGuiVisitor::visit(ImageShader &n) { ImGui::PushID(n.id()); - if (ImGuiToolkit::ButtonIcon(2, 1)) { + if (ImGuiToolkit::ButtonIcon(4, 1)) { n.brightness = 0.f; n.contrast = 0.f; } ImGui::SameLine(0, 10); float bc[2] = { n.brightness, n.contrast}; - if ( ImGui::SliderFloat2("filter", bc, -1.0, 1.0) ) + if ( ImGui::SliderFloat2("B & C", bc, -1.0, 1.0) ) { n.brightness = bc[0]; n.contrast = bc[1]; @@ -134,6 +139,63 @@ void ImGuiVisitor::visit(ImageShader &n) ImGui::PopID(); } +void ImGuiVisitor::visit(ImageProcessingShader &n) +{ + ImGui::PushID(n.id()); + + if (ImGuiToolkit::ButtonIcon(4, 1)) { + n.brightness = 0.f; + n.contrast = 0.f; + } + ImGui::SameLine(0, 10); + float bc[2] = { n.brightness, n.contrast}; + if ( ImGui::SliderFloat2("B & C", bc, -1.0, 1.0) ) + { + n.brightness = bc[0]; + n.contrast = bc[1]; + } + + if (ImGuiToolkit::ButtonIcon(2, 1)) n.saturation = 0.f; + ImGui::SameLine(0, 10); + ImGui::SliderFloat("Saturation", &n.saturation, -1.0, 1.0); + + if (ImGuiToolkit::ButtonIcon(12, 4)) n.hueshift = 0.f; + ImGui::SameLine(0, 10); + ImGui::SliderFloat("Hue shift", &n.hueshift, 0.0, 1.0); + + if (ImGuiToolkit::ButtonIcon(8, 1)) n.threshold = 0.f; + ImGui::SameLine(0, 10); + ImGui::SliderFloat("Threshold", &n.threshold, 0.0, 1.0); + + if (ImGuiToolkit::ButtonIcon(3, 1)) n.lumakey = 0.f; + ImGui::SameLine(0, 10); + ImGui::SliderFloat("Lumakey", &n.lumakey, 0.0, 1.0); + + if (ImGuiToolkit::ButtonIcon(18, 1)) n.nbColors = 0; + ImGui::SameLine(0, 10); + ImGui::SliderInt("Posterize", &n.nbColors, 0, 16); + + if (ImGuiToolkit::ButtonIcon(1, 7)) n.filter = 0; + ImGui::SameLine(0, 10); + ImGui::Combo("Filter", &n.filter, "None\0Blur\0Sharpen\0Edge\0Emboss\0Erode 3x3\0Erode 5x5\0Erode 7x7\0Dilate 3x3\0Dilate 5x5\0Dilate 7x7\0"); + + if (ImGuiToolkit::ButtonIcon(7, 1)) n.invert = 0; + ImGui::SameLine(0, 10); + ImGui::Combo("Invert", &n.invert, "None\0Invert color\0Invert Luminance\0"); + + if (ImGuiToolkit::ButtonIcon(14, 4)) n.chromadelta = 0.f; + ImGui::SameLine(0, 10); + ImGui::SliderFloat("Chromakey", &n.chromadelta, 0.0, 1.0); + + if (ImGuiToolkit::ButtonIcon(6, 4)) + n.chromakey = glm::vec4(0.f, 1.f, 0.f, 1.f); + ImGui::SameLine(0, 10); + ImGui::ColorEdit3("Chroma color", glm::value_ptr(n.chromakey) ) ; + + + ImGui::PopID(); +} + void ImGuiVisitor::visit(Scene &n) { ImGui::SetNextItemOpen(true, ImGuiCond_Once); diff --git a/ImGuiVisitor.h b/ImGuiVisitor.h index 0ed9aaf..8b6cf50 100644 --- a/ImGuiVisitor.h +++ b/ImGuiVisitor.h @@ -25,9 +25,10 @@ public: void visit(Mesh&) {} // Elements with attributes - void visit(MediaPlayer& n); - void visit(Shader& n); - void visit(ImageShader& n); + void visit(MediaPlayer& n) override; + void visit(Shader& n) override; + void visit(ImageShader& n) override; + void visit(ImageProcessingShader& n) override; }; #endif // IMGUIVISITOR_H diff --git a/ImageProcessingShader.cpp b/ImageProcessingShader.cpp index 4cb515b..0ec345f 100644 --- a/ImageProcessingShader.cpp +++ b/ImageProcessingShader.cpp @@ -1,7 +1,9 @@ +#include "defines.h" +#include "Visitor.h" +#include "Log.h" #include "ImageProcessingShader.h" - -ShadingProgram imageProcessingShadingProgram("shaders/processing-shader.vs", "shaders/texture-shader.fs"); +ShadingProgram imageProcessingShadingProgram("shaders/image.vs", "shaders/imageprocessing.fs"); ImageProcessingShader::ImageProcessingShader() { @@ -13,9 +15,25 @@ void ImageProcessingShader::use() { Shader::use(); + program_->setUniform("iChannelResolution[0]", iChannelResolution[0].x, iChannelResolution[0].y, iChannelResolution[0].z); + program_->setUniform("iChannelResolution[1]", iChannelResolution[1].x, iChannelResolution[1].y, iChannelResolution[1].z); + program_->setUniform("brightness", brightness); program_->setUniform("contrast", contrast); - program_->setUniform("stipple", stipple); + program_->setUniform("saturation", saturation); + program_->setUniform("hueshift", hueshift); + + program_->setUniform("threshold", threshold); + program_->setUniform("lumakey", lumakey); + program_->setUniform("nbColors", nbColors); + program_->setUniform("invert", invert); + program_->setUniform("filter", filter); + + program_->setUniform("gamma", gamma); + program_->setUniform("levels", levels); + program_->setUniform("chromakey", chromakey); + program_->setUniform("chromadelta", chromadelta); + } @@ -23,18 +41,28 @@ void ImageProcessingShader::reset() { Shader::reset(); + // no texture resolution yet + iChannelResolution[0] = glm::vec3(1.f); + iChannelResolution[1] = glm::vec3(1.f); + + // default values for image processing brightness = 0.f; contrast = 0.f; - stipple = 0.f; + saturation = 0.f; + hueshift = 0.f; + threshold = 0.f; + lumakey = 0.f; + nbColors = 0; + invert = 0; + filter = 0; + gamma = glm::vec4(1.f, 1.f, 1.f, 1.f); + levels = glm::vec4(0.f, 1.f, 0.f, 1.f); + chromakey = glm::vec4(0.f, 1.f, 0.f, 0.f); + chromadelta = 0.f; + } void ImageProcessingShader::accept(Visitor& v) { Shader::accept(v); v.visit(*this); } - - -::ImageProcessingShader() -{ - -} diff --git a/ImageProcessingShader.h b/ImageProcessingShader.h index 3a4f1d1..9567338 100644 --- a/ImageProcessingShader.h +++ b/ImageProcessingShader.h @@ -1,6 +1,8 @@ #ifndef IMAGEPROCESSINGSHADER_H #define IMAGEPROCESSINGSHADER_H +#include + #include "Shader.h" class ImageProcessingShader : public Shader @@ -8,15 +10,37 @@ class ImageProcessingShader : public Shader public: ImageProcessingShader(); - virtual ~ImageShader() {} + virtual ~ImageProcessingShader() {} virtual void use(); virtual void reset(); virtual void accept(Visitor& v); - float brightness; - float contrast; - float stipple; + // textures resolution + glm::vec3 iChannelResolution[2]; + + // color effects + float brightness; // [-1 1] + float contrast; // [-1 1] + float saturation; // [-1 1] + float hueshift; // [0 1] + float threshold; // [0 1] + float lumakey; // [0 1] + // gamma + glm::vec4 gamma; + glm::vec4 levels; + // discrete operations + int nbColors; + int invert; + // chroma key + glm::vec4 chromakey; + float chromadelta; + // filter + // [0] No filter + // [1 4] 4 x kernel operations; Blur, Sharpen, Edge, Emboss + // [5 10] 6 x convolutions: erosion 3, 5, 7, dilation 3, 5, 7 + int filter; + }; diff --git a/ImageShader.cpp b/ImageShader.cpp index 8fdd1eb..e39e95d 100644 --- a/ImageShader.cpp +++ b/ImageShader.cpp @@ -2,7 +2,7 @@ #include "Visitor.h" #include "ImageShader.h" -ShadingProgram imageShadingProgram("shaders/texture-shader.vs", "shaders/texture-shader.fs"); +ShadingProgram imageShadingProgram("shaders/image.vs", "shaders/image.fs"); ImageShader::ImageShader() { diff --git a/PickingVisitor.h b/PickingVisitor.h index 325c8fa..1cf305c 100644 --- a/PickingVisitor.h +++ b/PickingVisitor.h @@ -31,11 +31,6 @@ public: void visit(LineSquare&); void visit(LineCircle& n); void visit(Mesh&){} - - // not picking other Elements with attributes - void visit(MediaPlayer&) {} - void visit(Shader&) {} - void visit(ImageShader&) {} }; #endif // PICKINGVISITOR_H diff --git a/SearchVisitor.h b/SearchVisitor.h index 21e84fb..d76788b 100644 --- a/SearchVisitor.h +++ b/SearchVisitor.h @@ -33,10 +33,6 @@ public: void visit(LineCircle&) {} void visit(Mesh&) {} - // not nodes - void visit(MediaPlayer&) {} - void visit(Shader&) {} - void visit(ImageShader&) {} }; #endif // SEARCHVISITOR_H diff --git a/SessionVisitor.cpp b/SessionVisitor.cpp index 1757440..a957c0e 100644 --- a/SessionVisitor.cpp +++ b/SessionVisitor.cpp @@ -5,6 +5,7 @@ #include "Primitives.h" #include "Mesh.h" #include "ImageShader.h" +#include "ImageProcessingShader.h" #include "MediaPlayer.h" #include "GstToolkit.h" @@ -157,13 +158,46 @@ void SessionVisitor::visit(ImageShader &n) // Shader of a textured type xmlCurrent_->SetAttribute("type", "ImageShader"); - XMLElement *filter = xmlDoc_->NewElement("filter"); + XMLElement *filter = xmlDoc_->NewElement("uniforms"); filter->SetAttribute("brightness", n.brightness); filter->SetAttribute("contrast", n.contrast); + filter->SetAttribute("stipple", n.stipple); xmlCurrent_->InsertEndChild(filter); } +void SessionVisitor::visit(ImageProcessingShader &n) +{ + // Shader of a textured type + xmlCurrent_->SetAttribute("type", "ImageProcessingShader"); + + XMLElement *filter = xmlDoc_->NewElement("uniforms"); + filter->SetAttribute("brightness", n.brightness); + filter->SetAttribute("contrast", n.contrast); + filter->SetAttribute("saturation", n.saturation); + filter->SetAttribute("hueshift", n.hueshift); + filter->SetAttribute("threshold", n.threshold); + filter->SetAttribute("lumakey", n.lumakey); + filter->SetAttribute("nbColors", n.nbColors); + filter->SetAttribute("invertMode", n.invert); + filter->SetAttribute("chromadelta", n.chromadelta); + filter->SetAttribute("filter", n.filter); + xmlCurrent_->InsertEndChild(filter); + + XMLElement *gamma = XMLElementFromGLM(xmlDoc_, n.gamma); + gamma->SetAttribute("type", "gamma"); + xmlCurrent_->InsertEndChild(gamma); + + XMLElement *levels = XMLElementFromGLM(xmlDoc_, n.levels); + levels->SetAttribute("type", "levels"); + xmlCurrent_->InsertEndChild(levels); + + XMLElement *chromakey = XMLElementFromGLM(xmlDoc_, n.chromakey); + chromakey->SetAttribute("type", "chromakey"); + xmlCurrent_->InsertEndChild(chromakey); + +} + void SessionVisitor::visit(LineStrip &n) { // Node of a different type diff --git a/SessionVisitor.h b/SessionVisitor.h index 5609ce0..221b711 100644 --- a/SessionVisitor.h +++ b/SessionVisitor.h @@ -35,6 +35,7 @@ public: void visit(MediaPlayer& n); void visit(Shader& n); void visit(ImageShader& n); + void visit(ImageProcessingShader& n); }; #endif // XMLVISITOR_H diff --git a/Shader.cpp b/Shader.cpp index 736622c..350cbc5 100644 --- a/Shader.cpp +++ b/Shader.cpp @@ -20,7 +20,7 @@ // Globals ShadingProgram *ShadingProgram::currentProgram_ = nullptr; -ShadingProgram simpleShadingProgram("shaders/simple-shader.vs", "shaders/simple-shader.fs"); +ShadingProgram simpleShadingProgram("shaders/simple.vs", "shaders/simple.fs"); // Blending presets for matching with Shader::BlendMode GLenum blending_equation[6] = { GL_FUNC_ADD, GL_FUNC_ADD, GL_FUNC_REVERSE_SUBTRACT, GL_FUNC_ADD, GL_FUNC_REVERSE_SUBTRACT, GL_FUNC_ADD}; @@ -194,8 +194,8 @@ void Shader::use() program_->setUniform("modelview", modelview); program_->setUniform("color", color); - resolution = glm::vec3( Rendering::manager().currentAttrib().viewport, 0.f); - program_->setUniform("resolution", resolution); + iResolution = glm::vec3( Rendering::manager().currentAttrib().viewport, 0.f); + program_->setUniform("iResolution", iResolution); // Blending Function if ( blending != BLEND_NONE) { @@ -212,7 +212,7 @@ void Shader::reset() { projection = glm::identity(); modelview = glm::identity(); - resolution = glm::vec3(1280.f, 720.f, 0.f); + iResolution = glm::vec3(1280.f, 720.f, 0.f); color = glm::vec4(1.f, 1.f, 1.f, 1.f); } diff --git a/Shader.h b/Shader.h index f791e08..8c65547 100644 --- a/Shader.h +++ b/Shader.h @@ -66,7 +66,7 @@ public: protected: ShadingProgram *program_; - glm::vec3 resolution; + glm::vec3 iResolution; }; diff --git a/Source.cpp b/Source.cpp index fce032e..ef47ae9 100644 --- a/Source.cpp +++ b/Source.cpp @@ -7,6 +7,7 @@ #include "defines.h" #include "FrameBuffer.h" #include "ImageShader.h" +#include "ImageProcessingShader.h" #include "Resource.h" #include "Primitives.h" #include "Mesh.h" @@ -23,7 +24,6 @@ Source::Source(std::string name) : name_(""), initialized_(false) rename(name); // create groups for each view - // default rendering node groups_[View::RENDERING] = new Group; groups_[View::RENDERING]->scale_ = glm::vec3(5.f, 5.f, 1.f); // fit height full window @@ -35,12 +35,18 @@ Source::Source(std::string name) : name_(""), initialized_(false) groups_[View::MIXING]->addChild(frame); groups_[View::MIXING]->scale_ = glm::vec3(0.2f, 0.2f, 1.f); + // will be associated to nodes later + rendershader_ = new ImageProcessingShader(); + // add source to the list sources_.push_front(this); } Source::~Source() { + // delete shader + delete rendershader_; + // delete groups and their children delete groups_[View::RENDERING]; delete groups_[View::MIXING]; @@ -148,14 +154,6 @@ MediaSource::~MediaSource() // TODO verify that all surfaces and node is deleted in Source destructor } -ImageShader *MediaSource::shader() const -{ - if (!rendersurface_) - return nullptr; - - return static_cast(rendersurface_->shader()); -} - std::string MediaSource::uri() const { return uri_; @@ -182,9 +180,12 @@ void MediaSource::init() // create Frame buffer matching size of media player renderbuffer_ = new FrameBuffer(mediaplayer()->width(), mediaplayer()->height()); + // setup shader resolution for texture 0 + rendershader_->iChannelResolution[0] = glm::vec3(mediaplayer()->width(), mediaplayer()->height(), 0.f); + // create the surfaces to draw the frame buffer in the views // TODO Provide the source specific effect shader - rendersurface_ = new FrameBufferSurface(renderbuffer_); + rendersurface_ = new FrameBufferSurface(renderbuffer_, rendershader_); groups_[View::RENDERING]->addChild(rendersurface_); groups_[View::MIXING]->addChild(rendersurface_); @@ -225,7 +226,7 @@ void MediaSource::render(bool current) // read position of the mixing node and interpret this as transparency of render output float alpha = 1.0 - CLAMP( SQUARE( glm::length(groups_[View::MIXING]->translation_) ), 0.f, 1.f ); - rendersurface_->shader()->color.a = alpha; + rendershader_->color.a = alpha; // make Mixing Overlay visible if it is current source diff --git a/Source.h b/Source.h index dbce268..ce8c460 100644 --- a/Source.h +++ b/Source.h @@ -7,7 +7,7 @@ #include "View.h" -class ImageShader; +class ImageProcessingShader; class Surface; class FrameBuffer; class MediaPlayer; @@ -35,7 +35,7 @@ public: inline Group *group(View::Mode m) const { return groups_.at(m); } // every Source have a shader to control visual effects - virtual ImageShader *shader() const = 0; + inline ImageProcessingShader *shader() const { return rendershader_; } // every Source shall be rendered before draw virtual void render(bool current) = 0; @@ -64,10 +64,14 @@ protected: FrameBuffer *renderbuffer_; // the rendersurface draws the renderbuffer in the scene - // It is associated to the sourceshader for mixing effects + // It is associated to the rendershader for mixing effects // (aka visual effect applied in scene, not in render() ) FrameBufferSurface *rendersurface_; + // rendershader provides all image processing controls for + // mixing sources in the scene + ImageProcessingShader *rendershader_; + // static global list of sources static SourceList sources_; }; @@ -101,9 +105,6 @@ public: void render(bool current); - // Source interface - ImageShader *shader() const; - // Media specific interface std::string uri() const; MediaPlayer *mediaplayer() const; diff --git a/Visitor.h b/Visitor.h index 63a71a7..64d90fc 100644 --- a/Visitor.h +++ b/Visitor.h @@ -21,6 +21,7 @@ class Mesh; class MediaPlayer; class Shader; class ImageShader; +class ImageProcessingShader; // Declares the interface for the visitors class Visitor { @@ -42,9 +43,11 @@ public: virtual void visit (LineCircle&) = 0; virtual void visit (Mesh&) = 0; - virtual void visit (MediaPlayer&) = 0; - virtual void visit (Shader&) = 0; - virtual void visit (ImageShader&) = 0; + // not mandatory + virtual void visit (MediaPlayer&) {} + virtual void visit (Shader&) {} + virtual void visit (ImageShader&) {} + virtual void visit (ImageProcessingShader&) {} }; diff --git a/main.cpp b/main.cpp index 9565d0f..d6f5340 100644 --- a/main.cpp +++ b/main.cpp @@ -33,6 +33,7 @@ #include "RenderingManager.h" #include "UserInterfaceManager.h" #include "FrameBuffer.h" +#include "ImageProcessingShader.h" #include "MediaPlayer.h" #include "Scene.h" @@ -189,8 +190,14 @@ void drawScene() // draw GUI tree scene ImGui::Begin(IMGUI_TITLE_MAINWINDOW); static ImGuiVisitor v; - Mixer::manager().currentView()->scene.accept(v); +// Mixer::manager().currentView()->scene.accept(v); // Mixer::manager().getView(View::RENDERING)->scene.accept(v); + + Source *s = Mixer::manager().currentSource(); + if ( s != nullptr) { + s->shader()->accept(v); + } + ImGui::End(); } diff --git a/rsc/images/shadow.png b/rsc/images/shadow.png new file mode 100755 index 0000000000000000000000000000000000000000..f0a350a5ccd680d95ede06f6aaea1b876914be9c GIT binary patch literal 5314 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 Uif5!$^8f$<07*qoM6N<$f+%UQu>b%7 literal 0 HcmV?d00001 diff --git a/rsc/images/shadow_dark.png b/rsc/images/shadow_dark.png new file mode 100755 index 0000000000000000000000000000000000000000..e721a800349a3a3e04e73b1e8d9a3b3005f944b6 GIT binary patch literal 6383 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 10) + return texture(iChannel0, vertexUV).rgb; + else if (filter < 5) + return convolution( KERNEL[filter], filter_step); + else if (filter < 8) + return erosion( 3 + (filter -5) * 2, filter_step); + else + return dilation( 3 + (filter -8) * 2, filter_step); +} + +/* +** Hue, saturation, luminance <=> Red Green Blue +*/ + +float HueToRGB(float f1, float f2, float hue) +{ + float res; + + hue += mix( -float( hue > 1.0 ), 1.0, float(hue < 0.0) ); + + res = mix( f1, mix( clamp( f1 + (f2 - f1) * ((2.0 / 3.0) - hue) * 6.0, 0.0, 1.0) , mix( f2, clamp(f1 + (f2 - f1) * 6.0 * hue, 0.0, 1.0), float((6.0 * hue) < 1.0)), float((2.0 * hue) < 1.0)), float((3.0 * hue) < 2.0) ); + + return res; +} + +vec3 HSV2RGB(vec3 hsl) +{ + vec3 rgb; + float f1, f2; + + f2 = mix( (hsl.z + hsl.y) - (hsl.y * hsl.z), hsl.z * (1.0 + hsl.y), float(hsl.z < 0.5) ); + + f1 = 2.0 * hsl.z - f2; + + rgb.r = HueToRGB(f1, f2, hsl.x + ONETHIRD); + rgb.g = HueToRGB(f1, f2, hsl.x); + rgb.b = HueToRGB(f1, f2, hsl.x - ONETHIRD); + + rgb = mix( rgb, vec3(hsl.z), float(hsl.y < EPSILON)); + + return rgb; +} + +vec3 RGB2HSV( vec3 color ) +{ + vec3 hsl = vec3(0.0); // init to 0 + + float fmin = min(min(color.r, color.g), color.b); //Min. value of RGB + float fmax = max(max(color.r, color.g), color.b); //Max. value of RGB + float delta = fmax - fmin + EPSILON; //Delta RGB value + + vec3 deltaRGB = ( ( vec3(fmax) - color ) / 6.0 + vec3(delta) / 2.0 ) / delta ; + + hsl.z = (fmax + fmin) / 2.0; // Luminance + + hsl.y = delta / ( EPSILON + mix( 2.0 - fmax - fmin, fmax + fmin, float(hsl.z < 0.5)) ); + + hsl.x = mix( hsl.x, TWOTHIRD + deltaRGB.g - deltaRGB.r, float(color.b == fmax)); + hsl.x = mix( hsl.x, ONETHIRD + deltaRGB.r - deltaRGB.b, float(color.g == fmax)); + hsl.x = mix( hsl.x, deltaRGB.b - deltaRGB.g, float(color.r == fmax)); + + hsl.x += mix( - float( hsl.x > 1.0 ), 1.0, float(hsl.x < 0.0) ); + + hsl = mix ( hsl, vec3(-1.0, 0.0, hsl.z), float(delta 0.0001) ); + + // color transformation + transformedRGB = mix(vec3(0.62), transformedRGB, contrast + 1.0) + brightness; + transformedRGB = LevelsControl(transformedRGB, levels.x, gamma.rgb * gamma.a, levels.y, levels.z, levels.w); + + // RGB invert + transformedRGB = vec3(float(invert==1)) + ( transformedRGB * vec3(1.0 - 2.0 * float(invert==1)) ); + + // Convert to HSL + vec3 transformedHSL = RGB2HSV( transformedRGB ); + + // Luminance invert + transformedHSL.z = float(invert==2) + transformedHSL.z * (1.0 - 2.0 * float(invert==2) ); + + // perform hue shift + transformedHSL.x = transformedHSL.x + hueshift; + + // Saturation + transformedHSL.y *= saturation + 1.0; + + // perform reduction of colors + transformedHSL = mix( transformedHSL, floor(transformedHSL * vec3(nbColors)) / vec3(nbColors-1), float( nbColors > 0 ) ); + + // luma key + alpha -= mix( 0.0, step( transformedHSL.z, lumakey ), float(lumakey > EPSILON)); + + // level threshold + transformedHSL = mix( transformedHSL, vec3(0.0, 0.0, step( transformedHSL.z, threshold )), float(threshold > EPSILON)); + + // after operations on HSL, convert back to RGB + transformedRGB = HSV2RGB(transformedHSL); + + // apply base color and alpha for final fragment color + FragColor = vec4(transformedRGB * vertexColor.rgb * color.rgb, clamp(alpha, 0.0, 1.0) ); + +} + + diff --git a/rsc/shaders/simple-shader.fs b/rsc/shaders/simple.fs similarity index 71% rename from rsc/shaders/simple-shader.fs rename to rsc/shaders/simple.fs index 2a16f3f..d18a94b 100644 --- a/rsc/shaders/simple-shader.fs +++ b/rsc/shaders/simple.fs @@ -5,7 +5,7 @@ out vec4 FragColor; in vec4 vertexColor; uniform vec4 color; // drawing color -uniform vec3 resolution; // viewport resolution (in pixels) +uniform vec3 iResolution; // viewport resolution (in pixels) void main() { diff --git a/rsc/shaders/simple-shader.vs b/rsc/shaders/simple.vs similarity index 100% rename from rsc/shaders/simple-shader.vs rename to rsc/shaders/simple.vs