diff --git a/example/hyprland.conf b/example/hyprland.conf index 698335ea..0f830ec3 100644 --- a/example/hyprland.conf +++ b/example/hyprland.conf @@ -63,6 +63,8 @@ decoration { enabled = true size = 3 passes = 1 + + vibrancy = 0.1696 } drop_shadow = true diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 760e9e87..d07e6a6a 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -153,34 +153,36 @@ void CConfigManager::setDefaultVars() { configValues["debug:suppress_errors"].intValue = 0; configValues["debug:watchdog_timeout"].intValue = 5; - configValues["decoration:rounding"].intValue = 0; - configValues["decoration:blur:enabled"].intValue = 1; - configValues["decoration:blur:size"].intValue = 8; - configValues["decoration:blur:passes"].intValue = 1; - configValues["decoration:blur:ignore_opacity"].intValue = 0; - configValues["decoration:blur:new_optimizations"].intValue = 1; - configValues["decoration:blur:xray"].intValue = 0; - configValues["decoration:blur:noise"].floatValue = 0.0117; - configValues["decoration:blur:contrast"].floatValue = 0.8916; - configValues["decoration:blur:brightness"].floatValue = 0.8172; - configValues["decoration:blur:special"].intValue = 0; - configValues["decoration:active_opacity"].floatValue = 1; - configValues["decoration:inactive_opacity"].floatValue = 1; - configValues["decoration:fullscreen_opacity"].floatValue = 1; - configValues["decoration:no_blur_on_oversized"].intValue = 0; - configValues["decoration:drop_shadow"].intValue = 1; - configValues["decoration:shadow_range"].intValue = 4; - configValues["decoration:shadow_render_power"].intValue = 3; - configValues["decoration:shadow_ignore_window"].intValue = 1; - configValues["decoration:shadow_offset"].vecValue = Vector2D(); - configValues["decoration:shadow_scale"].floatValue = 1.f; - configValues["decoration:col.shadow"].intValue = 0xee1a1a1a; - configValues["decoration:col.shadow_inactive"].intValue = INT_MAX; - configValues["decoration:dim_inactive"].intValue = 0; - configValues["decoration:dim_strength"].floatValue = 0.5f; - configValues["decoration:dim_special"].floatValue = 0.2f; - configValues["decoration:dim_around"].floatValue = 0.4f; - configValues["decoration:screen_shader"].strValue = STRVAL_EMPTY; + configValues["decoration:rounding"].intValue = 0; + configValues["decoration:blur:enabled"].intValue = 1; + configValues["decoration:blur:size"].intValue = 8; + configValues["decoration:blur:passes"].intValue = 1; + configValues["decoration:blur:ignore_opacity"].intValue = 0; + configValues["decoration:blur:new_optimizations"].intValue = 1; + configValues["decoration:blur:xray"].intValue = 0; + configValues["decoration:blur:contrast"].floatValue = 0.8916; + configValues["decoration:blur:brightness"].floatValue = 1.0; + configValues["decoration:blur:vibrancy"].floatValue = 0.1696; + configValues["decoration:blur:vibrancy_darkness"].floatValue = 0.0; + configValues["decoration:blur:noise"].floatValue = 0.0117; + configValues["decoration:blur:special"].intValue = 0; + configValues["decoration:active_opacity"].floatValue = 1; + configValues["decoration:inactive_opacity"].floatValue = 1; + configValues["decoration:fullscreen_opacity"].floatValue = 1; + configValues["decoration:no_blur_on_oversized"].intValue = 0; + configValues["decoration:drop_shadow"].intValue = 1; + configValues["decoration:shadow_range"].intValue = 4; + configValues["decoration:shadow_render_power"].intValue = 3; + configValues["decoration:shadow_ignore_window"].intValue = 1; + configValues["decoration:shadow_offset"].vecValue = Vector2D(); + configValues["decoration:shadow_scale"].floatValue = 1.f; + configValues["decoration:col.shadow"].intValue = 0xee1a1a1a; + configValues["decoration:col.shadow_inactive"].intValue = INT_MAX; + configValues["decoration:dim_inactive"].intValue = 0; + configValues["decoration:dim_strength"].floatValue = 0.5f; + configValues["decoration:dim_special"].floatValue = 0.2f; + configValues["decoration:dim_around"].floatValue = 0.4f; + configValues["decoration:screen_shader"].strValue = STRVAL_EMPTY; configValues["dwindle:pseudotile"].intValue = 0; configValues["dwindle:force_split"].intValue = 0; diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index 690b0ede..e047f287 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -307,15 +307,18 @@ void CHyprOpenGLImpl::initShaders() { m_RenderData.pCurrentMonData->m_shEXT.applyTint = glGetUniformLocation(prog, "applyTint"); m_RenderData.pCurrentMonData->m_shEXT.tint = glGetUniformLocation(prog, "tint"); - prog = createProgram(TEXVERTSRC, FRAGBLUR1); - m_RenderData.pCurrentMonData->m_shBLUR1.program = prog; - m_RenderData.pCurrentMonData->m_shBLUR1.tex = glGetUniformLocation(prog, "tex"); - m_RenderData.pCurrentMonData->m_shBLUR1.alpha = glGetUniformLocation(prog, "alpha"); - m_RenderData.pCurrentMonData->m_shBLUR1.proj = glGetUniformLocation(prog, "proj"); - m_RenderData.pCurrentMonData->m_shBLUR1.posAttrib = glGetAttribLocation(prog, "pos"); - m_RenderData.pCurrentMonData->m_shBLUR1.texAttrib = glGetAttribLocation(prog, "texcoord"); - m_RenderData.pCurrentMonData->m_shBLUR1.radius = glGetUniformLocation(prog, "radius"); - m_RenderData.pCurrentMonData->m_shBLUR1.halfpixel = glGetUniformLocation(prog, "halfpixel"); + prog = createProgram(TEXVERTSRC, FRAGBLUR1); + m_RenderData.pCurrentMonData->m_shBLUR1.program = prog; + m_RenderData.pCurrentMonData->m_shBLUR1.tex = glGetUniformLocation(prog, "tex"); + m_RenderData.pCurrentMonData->m_shBLUR1.alpha = glGetUniformLocation(prog, "alpha"); + m_RenderData.pCurrentMonData->m_shBLUR1.proj = glGetUniformLocation(prog, "proj"); + m_RenderData.pCurrentMonData->m_shBLUR1.posAttrib = glGetAttribLocation(prog, "pos"); + m_RenderData.pCurrentMonData->m_shBLUR1.texAttrib = glGetAttribLocation(prog, "texcoord"); + m_RenderData.pCurrentMonData->m_shBLUR1.radius = glGetUniformLocation(prog, "radius"); + m_RenderData.pCurrentMonData->m_shBLUR1.halfpixel = glGetUniformLocation(prog, "halfpixel"); + m_RenderData.pCurrentMonData->m_shBLUR1.passes = glGetUniformLocation(prog, "passes"); + m_RenderData.pCurrentMonData->m_shBLUR1.vibrancy = glGetUniformLocation(prog, "vibrancy"); + m_RenderData.pCurrentMonData->m_shBLUR1.vibrancy_darkness = glGetUniformLocation(prog, "vibrancy_darkness"); prog = createProgram(TEXVERTSRC, FRAGBLUR2); m_RenderData.pCurrentMonData->m_shBLUR2.program = prog; @@ -327,15 +330,23 @@ void CHyprOpenGLImpl::initShaders() { m_RenderData.pCurrentMonData->m_shBLUR2.radius = glGetUniformLocation(prog, "radius"); m_RenderData.pCurrentMonData->m_shBLUR2.halfpixel = glGetUniformLocation(prog, "halfpixel"); + prog = createProgram(TEXVERTSRC, FRAGBLURPREPARE); + m_RenderData.pCurrentMonData->m_shBLURPREPARE.program = prog; + m_RenderData.pCurrentMonData->m_shBLURPREPARE.tex = glGetUniformLocation(prog, "tex"); + m_RenderData.pCurrentMonData->m_shBLURPREPARE.proj = glGetUniformLocation(prog, "proj"); + m_RenderData.pCurrentMonData->m_shBLURPREPARE.posAttrib = glGetAttribLocation(prog, "pos"); + m_RenderData.pCurrentMonData->m_shBLURPREPARE.texAttrib = glGetAttribLocation(prog, "texcoord"); + m_RenderData.pCurrentMonData->m_shBLURPREPARE.contrast = glGetUniformLocation(prog, "contrast"); + m_RenderData.pCurrentMonData->m_shBLURPREPARE.brightness = glGetUniformLocation(prog, "brightness"); + prog = createProgram(TEXVERTSRC, FRAGBLURFINISH); m_RenderData.pCurrentMonData->m_shBLURFINISH.program = prog; m_RenderData.pCurrentMonData->m_shBLURFINISH.tex = glGetUniformLocation(prog, "tex"); m_RenderData.pCurrentMonData->m_shBLURFINISH.proj = glGetUniformLocation(prog, "proj"); m_RenderData.pCurrentMonData->m_shBLURFINISH.posAttrib = glGetAttribLocation(prog, "pos"); m_RenderData.pCurrentMonData->m_shBLURFINISH.texAttrib = glGetAttribLocation(prog, "texcoord"); - m_RenderData.pCurrentMonData->m_shBLURFINISH.noise = glGetUniformLocation(prog, "noise"); - m_RenderData.pCurrentMonData->m_shBLURFINISH.contrast = glGetUniformLocation(prog, "contrast"); m_RenderData.pCurrentMonData->m_shBLURFINISH.brightness = glGetUniformLocation(prog, "brightness"); + m_RenderData.pCurrentMonData->m_shBLURFINISH.noise = glGetUniformLocation(prog, "noise"); prog = createProgram(QUADVERTSRC, FRAGSHADOW); m_RenderData.pCurrentMonData->m_shSHADOW.program = prog; @@ -912,8 +923,10 @@ CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* o wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix); // get the config settings - static auto* const PBLURSIZE = &g_pConfigManager->getConfigValuePtr("decoration:blur:size")->intValue; - static auto* const PBLURPASSES = &g_pConfigManager->getConfigValuePtr("decoration:blur:passes")->intValue; + static auto* const PBLURSIZE = &g_pConfigManager->getConfigValuePtr("decoration:blur:size")->intValue; + static auto* const PBLURPASSES = &g_pConfigManager->getConfigValuePtr("decoration:blur:passes")->intValue; + static auto* const PBLURVIBRANCY = &g_pConfigManager->getConfigValuePtr("decoration:blur:vibrancy")->floatValue; + static auto* const PBLURVIBRANCYDARKNESS = &g_pConfigManager->getConfigValuePtr("decoration:blur:vibrancy_darkness")->floatValue; // prep damage CRegion damage{*originalDamage}; @@ -927,7 +940,7 @@ CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* o CFramebuffer* currentRenderToFB = PMIRRORFB; - // begin with color adjustments + // Begin with base color adjustments - global brightness and contrast // TODO: make this a part of the first pass maybe to save on a drawcall? { static auto* const PBLURCONTRAST = &g_pConfigManager->getConfigValuePtr("decoration:blur:contrast")->floatValue; @@ -941,24 +954,23 @@ CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* o glTexParameteri(m_RenderData.pCurrentMonData->primaryFB.m_cTex.m_iTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glUseProgram(m_RenderData.pCurrentMonData->m_shBLURFINISH.program); + glUseProgram(m_RenderData.pCurrentMonData->m_shBLURPREPARE.program); #ifndef GLES2 - glUniformMatrix3fv(m_RenderData.pCurrentMonData->m_shBLURFINISH.proj, 1, GL_TRUE, glMatrix); + glUniformMatrix3fv(m_RenderData.pCurrentMonData->m_shBLURPREPARE.proj, 1, GL_TRUE, glMatrix); #else wlr_matrix_transpose(glMatrix, glMatrix); glUniformMatrix3fv(m_RenderData.pCurrentMonData->m_shBLURFINISH.proj, 1, GL_FALSE, glMatrix); #endif - glUniform1f(m_RenderData.pCurrentMonData->m_shBLURFINISH.contrast, *PBLURCONTRAST); - glUniform1f(m_RenderData.pCurrentMonData->m_shBLURFINISH.brightness, *PBLURBRIGHTNESS); + glUniform1f(m_RenderData.pCurrentMonData->m_shBLURPREPARE.contrast, *PBLURCONTRAST); + glUniform1f(m_RenderData.pCurrentMonData->m_shBLURPREPARE.brightness, *PBLURBRIGHTNESS); + glUniform1i(m_RenderData.pCurrentMonData->m_shBLURPREPARE.tex, 0); - glUniform1i(m_RenderData.pCurrentMonData->m_shBLURFINISH.tex, 0); + glVertexAttribPointer(m_RenderData.pCurrentMonData->m_shBLURPREPARE.posAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); + glVertexAttribPointer(m_RenderData.pCurrentMonData->m_shBLURPREPARE.texAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); - glVertexAttribPointer(m_RenderData.pCurrentMonData->m_shBLURFINISH.posAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); - glVertexAttribPointer(m_RenderData.pCurrentMonData->m_shBLURFINISH.texAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); - - glEnableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBLURFINISH.posAttrib); - glEnableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBLURFINISH.texAttrib); + glEnableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBLURPREPARE.posAttrib); + glEnableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBLURPREPARE.texAttrib); if (!damage.empty()) { for (auto& RECT : damage.getRects()) { @@ -967,8 +979,8 @@ CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* o } } - glDisableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBLURFINISH.posAttrib); - glDisableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBLURFINISH.texAttrib); + glDisableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBLURPREPARE.posAttrib); + glDisableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBLURPREPARE.texAttrib); currentRenderToFB = PMIRRORSWAPFB; } @@ -996,10 +1008,13 @@ CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* o glUniformMatrix3fv(pShader->proj, 1, GL_FALSE, glMatrix); #endif glUniform1f(pShader->radius, *PBLURSIZE * a); // this makes the blursize change with a - if (pShader == &m_RenderData.pCurrentMonData->m_shBLUR1) + if (pShader == &m_RenderData.pCurrentMonData->m_shBLUR1) { glUniform2f(m_RenderData.pCurrentMonData->m_shBLUR1.halfpixel, 0.5f / (m_RenderData.pMonitor->vecPixelSize.x / 2.f), 0.5f / (m_RenderData.pMonitor->vecPixelSize.y / 2.f)); - else + glUniform1i(m_RenderData.pCurrentMonData->m_shBLUR1.passes, *PBLURPASSES); + glUniform1f(m_RenderData.pCurrentMonData->m_shBLUR1.vibrancy, *PBLURVIBRANCY); + glUniform1f(m_RenderData.pCurrentMonData->m_shBLUR1.vibrancy_darkness, *PBLURVIBRANCYDARKNESS); + } else glUniform2f(m_RenderData.pCurrentMonData->m_shBLUR2.halfpixel, 0.5f / (m_RenderData.pMonitor->vecPixelSize.x * 2.f), 0.5f / (m_RenderData.pMonitor->vecPixelSize.y * 2.f)); glUniform1i(pShader->tex, 0); @@ -1045,9 +1060,10 @@ CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* o drawPass(&m_RenderData.pCurrentMonData->m_shBLUR2, &tempDamage); // up } - // finalize with noise + // finalize the image { - static auto* const PBLURNOISE = &g_pConfigManager->getConfigValuePtr("decoration:blur:noise")->floatValue; + static auto* const PBLURNOISE = &g_pConfigManager->getConfigValuePtr("decoration:blur:noise")->floatValue; + static auto* const PBLURBRIGHTNESS = &g_pConfigManager->getConfigValuePtr("decoration:blur:brightness")->floatValue; if (currentRenderToFB == PMIRRORFB) PMIRRORSWAPFB->bind(); @@ -1069,6 +1085,7 @@ CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* o glUniformMatrix3fv(m_RenderData.pCurrentMonData->m_shBLURFINISH.proj, 1, GL_FALSE, glMatrix); #endif glUniform1f(m_RenderData.pCurrentMonData->m_shBLURFINISH.noise, *PBLURNOISE); + glUniform1f(m_RenderData.pCurrentMonData->m_shBLURFINISH.brightness, *PBLURBRIGHTNESS); glUniform1i(m_RenderData.pCurrentMonData->m_shBLURFINISH.tex, 0); diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index 7e3e4e7c..0c6ef13c 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -64,6 +64,7 @@ struct SMonitorRenderData { CShader m_shEXT; CShader m_shBLUR1; CShader m_shBLUR2; + CShader m_shBLURPREPARE; CShader m_shBLURFINISH; CShader m_shSHADOW; CShader m_shBORDER1; diff --git a/src/render/Shader.hpp b/src/render/Shader.hpp index b67f61ef..70fe468c 100644 --- a/src/render/Shader.hpp +++ b/src/render/Shader.hpp @@ -46,13 +46,21 @@ class CShader { GLint distort = -1; GLint output = -1; - GLint noise = -1; - GLint contrast = -1; - GLint brightness = -1; + // Blur prepare + GLint contrast = -1; - GLint getUniformLocation(const std::string&); + // Blur + GLint passes = -1; // Used by `vibrancy` + GLint vibrancy = -1; + GLint vibrancy_darkness = -1; - void destroy(); + // Blur finish + GLint brightness = -1; + GLint noise = -1; + + GLint getUniformLocation(const std::string&); + + void destroy(); private: std::unordered_map m_muUniforms; diff --git a/src/render/shaders/Textures.hpp b/src/render/shaders/Textures.hpp index cf753bb4..15e80410 100644 --- a/src/render/shaders/Textures.hpp +++ b/src/render/shaders/Textures.hpp @@ -180,12 +180,116 @@ void main() { inline const std::string FRAGBLUR1 = R"#( #version 100 -precision mediump float; +precision mediump float; varying mediump vec2 v_texcoord; // is in 0-1 -uniform sampler2D tex; +uniform sampler2D tex; -uniform float radius; -uniform vec2 halfpixel; +uniform float radius; +uniform vec2 halfpixel; +uniform int passes; +uniform float vibrancy; +uniform float vibrancy_darkness; + +// see http://alienryderflex.com/hsp.html +const float Pr = 0.299; +const float Pg = 0.587; +const float Pb = 0.114; + +// Y is "v" ( brightness ). X is "s" ( saturation ) +// see https://www.desmos.com/3d/a88652b9a4 +// Determines if high brightness or high saturation is more important +const float a = 0.93; +const float b = 0.11; +const float c = 0.66; // Determines the smoothness of the transition of unboosted to boosted colors +// + +// http://www.flong.com/archive/texts/code/shapers_circ/ +float doubleCircleSigmoid(float x, float a) { + float min_param_a = 0.0; + float max_param_a = 1.0; + a = max(min_param_a, min(max_param_a, a)); + + float y = .0; + if (x <= a) { + y = a - sqrt(a * a - x * x); + } else { + y = a + sqrt(pow(1. - a, 2.) - pow(x - 1., 2.)); + } + return y; +} + +vec3 rgb2hsl(vec3 col) { + float red = col.r; + float green = col.g; + float blue = col.b; + + float minc = min(col.r, min(col.g, col.b)); + float maxc = max(col.r, max(col.g, col.b)); + float delta = maxc - minc; + + float lum = (minc + maxc) * 0.5; + float sat = 0.0; + float hue = 0.0; + + if (lum > 0.0 && lum < 1.0) { + float mul = (lum < 0.5) ? (lum) : (1.0 - lum); + sat = delta / (mul * 2.0); + } + + vec3 masks = vec3((maxc == red && maxc != green) ? 1.0 : 0.0, (maxc == green && maxc != blue) ? 1.0 : 0.0, (maxc == blue && maxc != red) ? 1.0 : 0.0); + + vec3 adds = vec3(((green - blue) / delta), 2.0 + ((blue - red) / delta), 4.0 + ((red - green) / delta)); + + float deltaGtz = (delta > 0.0) ? 1.0 : 0.0; + + hue += dot(adds, masks); + hue *= deltaGtz; + hue /= 6.0; + + if (hue < 0.0) + hue += 1.0; + + return vec3(hue, sat, lum); +} +vec3 hsl2rgb(vec3 col) { + const float onethird = 1.0 / 3.0; + const float twothird = 2.0 / 3.0; + const float rcpsixth = 6.0; + + float hue = col.x; + float sat = col.y; + float lum = col.z; + + vec3 xt = vec3(rcpsixth * (hue - twothird), 0.0, rcpsixth * (1.0 - hue)); + + if (hue < twothird) { + xt.r = 0.0; + xt.g = rcpsixth * (twothird - hue); + xt.b = rcpsixth * (hue - onethird); + } + + if (hue < onethird) { + xt.r = rcpsixth * (onethird - hue); + xt.g = rcpsixth * hue; + xt.b = 0.0; + } + + xt = min(xt, 1.0); + + float sat2 = 2.0 * sat; + float satinv = 1.0 - sat; + float luminv = 1.0 - lum; + float lum2m1 = (2.0 * lum) - 1.0; + vec3 ct = (sat2 * xt) + satinv; + + vec3 rgb; + if (lum >= 0.5) + rgb = (luminv * ct) + lum2m1; + else + rgb = lum * ct; + + return rgb; +} void main() { vec2 uv = v_texcoord * 2.0; @@ -196,7 +300,28 @@ void main() { sum += texture2D(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius); sum += texture2D(tex, uv - vec2(halfpixel.x, -halfpixel.y) * radius); - gl_FragColor = sum / 8.0; + vec4 color = sum / 8.0; + + if (vibrancy == 0.0) { + gl_FragColor = color; + } else { + // Invert it so that it correctly maps to the config setting + float vibrancy_darkness1 = 1.0 - vibrancy_darkness; + + // Decrease the RGB components based on their perceived brightness, to prevent visually dark colors from overblowing the rest. + vec3 hsl = rgb2hsl(color.rgb); + // Calculate perceived brightness, as not boost visually dark colors like deep blue as much as equally saturated yellow + float perceivedBrightness = doubleCircleSigmoid(sqrt(color.r * color.r * Pr + color.g * color.g * Pg + color.b * color.b * Pb), 0.8 * vibrancy_darkness1); + + float b1 = b * vibrancy_darkness1; + float boostBase = hsl[1] > 0.0 ? smoothstep(b1 - c * 0.5, b1 + c * 0.5, 1.0 - (pow(1.0 - hsl[1] * cos(a), 2.0) + pow(1.0 - perceivedBrightness * sin(a), 2.0))) : 0.0; + + float saturation = clamp(hsl[1] + (boostBase * vibrancy) / float(passes), 0.0, 1.0); + + vec3 newColor = hsl2rgb(vec3(hsl[0], saturation, hsl[2])); + + gl_FragColor = vec4(newColor, color[3]); + } } )#"; @@ -226,35 +351,66 @@ void main() { } )#"; -inline const std::string FRAGBLURFINISH = R"#( -precision mediump float; -varying vec2 v_texcoord; // is in 0-1 +inline const std::string FRAGBLURPREPARE = R"#( +precision mediump float; +varying vec2 v_texcoord; // is in 0-1 uniform sampler2D tex; -uniform float contrast; -uniform float noise; -uniform float brightness; +uniform float contrast; +uniform float brightness; -float hash(vec2 p) { - return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453); +float gain(float x, float k) { + float a = 0.5 * pow(2.0 * ((x < 0.5) ? x : 1.0 - x), k); + return (x < 0.5) ? a : 1.0 - a; } void main() { vec4 pixColor = texture2D(tex, v_texcoord); // contrast - pixColor.rgb = (pixColor.rgb - 0.5) * contrast + 0.5; + if (contrast != 1.0) { + pixColor.r = gain(pixColor.r, contrast); + pixColor.g = gain(pixColor.g, contrast); + pixColor.b = gain(pixColor.b, contrast); + } // brightness - pixColor.rgb *= brightness; + if (brightness > 1.0) { + pixColor.rgb *= brightness; + } + + gl_FragColor = pixColor; +} +)#"; + +inline const std::string FRAGBLURFINISH = R"#( +precision mediump float; +varying vec2 v_texcoord; // is in 0-1 +uniform sampler2D tex; + +uniform float noise; +uniform float brightness; + +float hash(vec2 p) { + return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453); +} + +void main() { + vec4 pixColor = texture2D(tex, v_texcoord); // noise - float noiseHash = hash(v_texcoord); + float noiseHash = hash(v_texcoord); float noiseAmount = (mod(noiseHash, 1.0) - 0.5); pixColor.rgb += noiseAmount * noise; + // brightness + if (brightness < 1.0) { + pixColor.rgb *= brightness; + } + gl_FragColor = pixColor; -})#"; +} +)#"; inline const std::string TEXFRAGSRCEXT = R"#( #extension GL_OES_EGL_image_external : require