From d5f2b375a61868cf04f9f04d1b1de56e911d3dfa Mon Sep 17 00:00:00 2001 From: Bruno Herbelin Date: Sun, 10 Mar 2024 11:51:13 +0100 Subject: [PATCH] New User defined uniforms in Custom shaders --- src/ControlManager.cpp | 21 ++++++++++++ src/ControlManager.h | 1 + src/ImGuiVisitor.cpp | 6 ++++ src/ImageFilter.cpp | 77 +++++++++++++++++++++++++++++++++++++++--- src/ImageFilter.h | 5 ++- src/Shader.cpp | 73 +++++++++++++++++++++++++++++---------- src/Shader.h | 8 ++--- 7 files changed, 164 insertions(+), 27 deletions(-) diff --git a/src/ControlManager.cpp b/src/ControlManager.cpp index dd46d5d..1c26cfe 100644 --- a/src/ControlManager.cpp +++ b/src/ControlManager.cpp @@ -967,6 +967,27 @@ bool Control::receiveSourceAttribute(Source *target, const std::string &attribut textsrc->contents()->setText(label); } } + /// e.g. '/vimix/current/uniform sf var 0.5' + else if (attribute.compare(OSC_SOURCE_UNIFORM) == 0) { + std::string uniform_name; + float uniform_value = NAN; + + const char *str = nullptr; + arguments >> str; + uniform_name = std::string(str); + + arguments >> uniform_value >> osc::EndMessage; + + + CloneSource *clonesrc = dynamic_cast(target); + if (clonesrc) { + ImageFilter *f = dynamic_cast(clonesrc->filter()); + if (f) { + f->setProgramParameter(uniform_name, uniform_value); + } + } + + } /// e.g. '/vimix/current/filter sf blur 0.5' else if (attribute.compare(OSC_SOURCE_FILTER) == 0) { std::string filter_name; diff --git a/src/ControlManager.h b/src/ControlManager.h index 743a720..5538cd3 100644 --- a/src/ControlManager.h +++ b/src/ControlManager.h @@ -71,6 +71,7 @@ #define OSC_SOURCE_TEXSIZE "/texture_size" #define OSC_SOURCE_TEXANGLE "/texture_angle" #define OSC_SOURCE_FILTER "/filter" +#define OSC_SOURCE_UNIFORM "/uniform" #define OSC_SESSION "/session" #define OSC_SESSION_VERSION "/version" diff --git a/src/ImGuiVisitor.cpp b/src/ImGuiVisitor.cpp index 6b47df4..074419d 100644 --- a/src/ImGuiVisitor.cpp +++ b/src/ImGuiVisitor.cpp @@ -1377,6 +1377,12 @@ void ImGuiVisitor::visit (ImageFilter& f) FilteringProgram target; f.setProgram( target ); } + + // List of parameters + std::ostringstream oss; + oss << "Custom "; + list_parameters_(f, oss); + } void ImGuiVisitor::visit (CloneSource& s) diff --git a/src/ImageFilter.cpp b/src/ImageFilter.cpp index 96bdfc1..f99e1aa 100644 --- a/src/ImageFilter.cpp +++ b/src/ImageFilter.cpp @@ -18,6 +18,7 @@ **/ #include #include +#include #include #include @@ -26,6 +27,7 @@ #include "Visitor.h" #include "FrameBuffer.h" #include "Primitives.h" +#include "BaseToolkit.h" #include "ImageFilter.h" @@ -150,6 +152,17 @@ bool FilteringProgram::operator!= (const FilteringProgram& other) const return false; } +bool FilteringProgram::hasParameter(const std::string &p) +{ + return parameters_.find(p) != parameters_.end(); +} + +void FilteringProgram::removeParameter(const std::string &p) +{ + if (hasParameter(p)) + parameters_.erase(p); +} + //////////////////////////////////////// ///// // @@ -168,6 +181,7 @@ ImageFilteringShader::ImageFilteringShader(): ImageShader() timer_ = g_timer_new (); iTime_ = 0.0; iFrame_ = 0; + uniforms_changed_ = true; ImageShader::reset(); } @@ -216,9 +230,17 @@ void ImageFilteringShader::use() // // loop over uniforms // - for (auto u = uniforms_.begin(); u != uniforms_.end(); ++u) + for (auto u = uniforms_.begin(); u != uniforms_.end(); ) { // set uniform to current value - program_->setUniform( u->first, u->second ); + if ( program_->setUniform(u->first, u->second) ) + // uniform variable could be set, keep it + ++u; + else { + // uniform variable does not exist in code, remove it + u = uniforms_.erase(u); + uniforms_changed_ = true; + } + } } void ImageFilteringShader::reset() @@ -242,7 +264,7 @@ void ImageFilteringShader::setCode(const std::string &code, std::promiseupdate(dt); + + // uniforms changed in main shader + if ( shaders_.first->uniforms_changed_ ) { + // loop over the parameters of the program... + std::map __P = program_.parameters(); + for (auto param = __P.begin(); param != __P.end(); ++param) { + // .. and remove the parameters that are not valid uniforms + if (shaders_.first->uniforms_.count(param->first) < 1) + program_.removeParameter(param->first); + } + // done + shaders_.first->uniforms_changed_ = false; + } } uint ImageFilter::texture () const @@ -392,6 +427,9 @@ FilteringProgram ImageFilter::program () const return program_; } +#define REGEX_UNIFORM_DECLARATION "uniform\\s+float\\s+" +#define REGEX_UNIFORM_VALUE "(\\s*=\\s*[[:digit:]](\\.[[:digit:]])?)?\\s*\\;" + void ImageFilter::setProgram(const FilteringProgram &f, std::promise *ret) { // always keep local copy @@ -404,11 +442,42 @@ void ImageFilter::setProgram(const FilteringProgram &f, std::promisesetCode( codes.first, ret ); + // Parse code to detect additional declaration of uniform variables + // Search for "uniform float", a variable name, with possibly a '=' and float value + std::string glslcode(codes.first); + std::smatch found_uniform; + std::regex is_a_uniform(REGEX_UNIFORM_DECLARATION "[[:alpha:]]+" REGEX_UNIFORM_VALUE); + // loop over every uniform declarations in the GLSL code + while (std::regex_search(glslcode, found_uniform, is_a_uniform)) { + // found a complete declaration of uniform variable + std::string declaration = found_uniform.str(); + // extract variable name by erasing everything else + std::string varname = + std::regex_replace(declaration,std::regex(REGEX_UNIFORM_DECLARATION),""); + varname = std::regex_replace(varname, std::regex(REGEX_UNIFORM_VALUE), ""); + // add to list of parameters if was not already there, with default value + if ( !program_.hasParameter(varname) ) + program_.setParameter(varname, 0.f); + + // try to find a value in uniform declaration, and set parameter value if valid + float val = 0.f; + std::smatch found_value; + std::regex is_a_float_value("[[:digit:]](\\.[[:digit:]])?"); + if (std::regex_search(declaration, found_value, is_a_float_value)) { + // set value only if a value is given + if ( BaseToolkit::is_a_value(found_value.str(), &val) ) + program_.setParameter(varname, val); + } + // keep parsing + glslcode = found_uniform.suffix().str(); + } + // SECOND PASS if ( program_.isTwoPass() ) // set the code to the shader for second-pass - shaders_.second->setCode( codes.second ); + shaders_.second->setCode(codes.second); + // UPDATE UNIFORMS updateParameters(); } diff --git a/src/ImageFilter.h b/src/ImageFilter.h index 34e8e7d..2fe5161 100644 --- a/src/ImageFilter.h +++ b/src/ImageFilter.h @@ -47,7 +47,7 @@ public: std::pair< std::string, std::string > code(); // if has second pass - bool isTwoPass() const { return two_pass_filter_; } + inline bool isTwoPass() const { return two_pass_filter_; } // set the list of parameters inline void setParameters(const std::map< std::string, float > ¶meters) { parameters_ = parameters; } @@ -57,6 +57,8 @@ public: // set the value of a parameter inline void setParameter(const std::string &p, float value) { parameters_[p] = value; } + bool hasParameter(const std::string &p); + void removeParameter(const std::string &p); // globals static std::string getFilterCodeInputs(); @@ -85,6 +87,7 @@ public: // list of uniforms to control shader std::map< std::string, float > uniforms_; + bool uniforms_changed_; ImageFilteringShader(); ~ImageFilteringShader(); diff --git a/src/Shader.cpp b/src/Shader.cpp index 817fa3f..6eb35f4 100644 --- a/src/Shader.cpp +++ b/src/Shader.cpp @@ -232,52 +232,89 @@ void ShadingProgram::reset() } template<> -void ShadingProgram::setUniform(const std::string& name, int val) { - glUniform1i(glGetUniformLocation(id_, name.c_str()), val); +bool ShadingProgram::setUniform(const std::string &name, int val) +{ + GLint uid = glGetUniformLocation(id_, name.c_str()); + if (uid < 0) + return false; + glUniform1i(uid, val); + return true; } template<> -void ShadingProgram::setUniform(const std::string& name, bool val) { - glUniform1i(glGetUniformLocation(id_, name.c_str()), val); +bool ShadingProgram::setUniform(const std::string& name, bool val) { + GLint uid = glGetUniformLocation(id_, name.c_str()); + if (uid < 0) + return false; + glUniform1i(uid, val); + return true; } template<> -void ShadingProgram::setUniform(const std::string& name, float val) { - glUniform1f(glGetUniformLocation(id_, name.c_str()), val); +bool ShadingProgram::setUniform(const std::string& name, float val) { + GLint uid = glGetUniformLocation(id_, name.c_str()); + if (uid < 0) + return false; + glUniform1f(uid, val); + return true; } template<> -void ShadingProgram::setUniform(const std::string& name, float val1, float val2) { - glUniform2f(glGetUniformLocation(id_, name.c_str()), val1, val2); +bool ShadingProgram::setUniform(const std::string& name, float val1, float val2) { + GLint uid = glGetUniformLocation(id_, name.c_str()); + if (uid < 0) + return false; + glUniform2f(uid, val1, val2); + return true; } template<> -void ShadingProgram::setUniform(const std::string& name, float val1, float val2, float val3) { - glUniform3f(glGetUniformLocation(id_, name.c_str()), val1, val2, val3); +bool ShadingProgram::setUniform(const std::string& name, float val1, float val2, float val3) { + GLint uid = glGetUniformLocation(id_, name.c_str()); + if (uid < 0) + return false; + glUniform3f(uid, val1, val2, val3); + return true; } template<> -void ShadingProgram::setUniform(const std::string& name, glm::vec2 val) { +bool ShadingProgram::setUniform(const std::string& name, glm::vec2 val) { glm::vec2 v(val); - glUniform2fv(glGetUniformLocation(id_, name.c_str()), 1, glm::value_ptr(v)); + GLint uid = glGetUniformLocation(id_, name.c_str()); + if (uid < 0) + return false; + glUniform2fv(uid, 1, glm::value_ptr(v)); + return true; } template<> -void ShadingProgram::setUniform(const std::string& name, glm::vec3 val) { +bool ShadingProgram::setUniform(const std::string& name, glm::vec3 val) { glm::vec3 v(val); - glUniform3fv(glGetUniformLocation(id_, name.c_str()), 1, glm::value_ptr(v)); + GLint uid = glGetUniformLocation(id_, name.c_str()); + if (uid < 0) + return false; + glUniform3fv(uid, 1, glm::value_ptr(v)); + return true; } template<> -void ShadingProgram::setUniform(const std::string& name, glm::vec4 val) { +bool ShadingProgram::setUniform(const std::string& name, glm::vec4 val) { glm::vec4 v(val); - glUniform4fv(glGetUniformLocation(id_, name.c_str()), 1, glm::value_ptr(v)); + GLint uid = glGetUniformLocation(id_, name.c_str()); + if (uid < 0) + return false; + glUniform4fv(uid, 1, glm::value_ptr(v)); + return true; } template<> -void ShadingProgram::setUniform(const std::string& name, glm::mat4 val) { +bool ShadingProgram::setUniform(const std::string& name, glm::mat4 val) { glm::mat4 m(val); - glUniformMatrix4fv(glGetUniformLocation(id_, name.c_str()), 1, GL_FALSE, glm::value_ptr(m)); + GLint uid = glGetUniformLocation(id_, name.c_str()); + if (uid < 0) + return false; + glUniformMatrix4fv(uid, 1, GL_FALSE, glm::value_ptr(m)); + return true; } diff --git a/src/Shader.h b/src/Shader.h index be3609b..813483c 100644 --- a/src/Shader.h +++ b/src/Shader.h @@ -25,9 +25,9 @@ public: static void enduse(); void reset(); - template void setUniform(const std::string& name, T val); - template void setUniform(const std::string& name, T val1, T val2); - template void setUniform(const std::string& name, T val1, T val2, T val3); + template bool setUniform(const std::string& name, T val); + template bool setUniform(const std::string& name, T val1, T val2); + template bool setUniform(const std::string& name, T val1, T val2, T val3); private: unsigned int id_; @@ -54,7 +54,7 @@ public: virtual void use(); virtual void reset(); virtual void accept(Visitor& v); - void copy(Shader const& S); + void copy(Shader const &S); glm::mat4 projection; glm::mat4 modelview;