From a2ca71ea55f878924213337b40f0d30ba774e964 Mon Sep 17 00:00:00 2001 From: German Date: Sun, 10 Aug 2025 03:01:32 +0200 Subject: [PATCH 1/4] refraction: add corner radius and mode settings, adjust maximums, add concave shader mode --- src/blur.cpp | 4 + src/blur.h | 2 + src/blur.kcfg | 6 + src/kcm/blur_config.cpp | 23 ++++ src/kcm/blur_config.ui | 170 ++++++++++++++++++++++----- src/settings.cpp | 15 ++- src/settings.h | 2 + src/shaders/texture.frag | 43 +++++++ src/shaders/texture_core.frag | 47 ++++++++ src/shaders/upsample.frag | 204 +++++++++++++++++++++++++++++++++ src/shaders/upsample.glsl | 120 +++++++++++++------ src/shaders/upsample_core.frag | 198 ++++++++++++++++++++++++++++++++ src/shaders/upsample_core.glsl | 111 ++++++++++++------ 13 files changed, 847 insertions(+), 98 deletions(-) create mode 100644 src/shaders/texture.frag create mode 100644 src/shaders/texture_core.frag create mode 100644 src/shaders/upsample.frag create mode 100644 src/shaders/upsample_core.frag diff --git a/src/blur.cpp b/src/blur.cpp index 03992db40..b2e37b845 100644 --- a/src/blur.cpp +++ b/src/blur.cpp @@ -97,10 +97,12 @@ BlurEffect::BlurEffect() m_upsamplePass.blurSizeLocation = m_upsamplePass.shader->uniformLocation("blurSize"); m_upsamplePass.opacityLocation = m_upsamplePass.shader->uniformLocation("opacity"); m_upsamplePass.edgeSizePixelsLocation = m_upsamplePass.shader->uniformLocation("edgeSizePixels"); + m_upsamplePass.refractionCornerRadiusPixelsLocation = m_upsamplePass.shader->uniformLocation("refractionCornerRadiusPixels"); m_upsamplePass.refractionStrengthLocation = m_upsamplePass.shader->uniformLocation("refractionStrength"); m_upsamplePass.refractionNormalPowLocation = m_upsamplePass.shader->uniformLocation("refractionNormalPow"); m_upsamplePass.refractionRGBFringingLocation = m_upsamplePass.shader->uniformLocation("refractionRGBFringing"); m_upsamplePass.refractionTextureRepeatModeLocation = m_upsamplePass.shader->uniformLocation("refractionTextureRepeatMode"); + m_upsamplePass.refractionModeLocation = m_upsamplePass.shader->uniformLocation("refractionMode"); } m_texture.shader = ShaderManager::instance()->generateShaderFromFile(ShaderTrait::MapTexture, @@ -1108,10 +1110,12 @@ void BlurEffect::blur(BlurRenderData &renderInfo, const RenderTarget &renderTarg if (w && m_settings.refraction.refractionStrength > 0) { m_upsamplePass.shader->setUniform(m_upsamplePass.edgeSizePixelsLocation, std::min(m_settings.refraction.edgeSizePixels, (float)std::min(deviceBackgroundRect.width() / 2, deviceBackgroundRect.height() / 2))); + m_upsamplePass.shader->setUniform(m_upsamplePass.refractionCornerRadiusPixelsLocation, m_settings.refraction.refractionCornerRadiusPixels); m_upsamplePass.shader->setUniform(m_upsamplePass.refractionStrengthLocation, m_settings.refraction.refractionStrength); m_upsamplePass.shader->setUniform(m_upsamplePass.refractionNormalPowLocation, m_settings.refraction.refractionNormalPow); m_upsamplePass.shader->setUniform(m_upsamplePass.refractionRGBFringingLocation, m_settings.refraction.refractionRGBFringing); m_upsamplePass.shader->setUniform(m_upsamplePass.refractionTextureRepeatModeLocation, m_settings.refraction.refractionTextureRepeatMode); + m_upsamplePass.shader->setUniform(m_upsamplePass.refractionModeLocation, m_settings.refraction.refractionMode); } glEnable(GL_BLEND); diff --git a/src/blur.h b/src/blur.h index c06a9ccfe..9ffcf9314 100644 --- a/src/blur.h +++ b/src/blur.h @@ -159,10 +159,12 @@ public Q_SLOTS: int opacityLocation; int edgeSizePixelsLocation; + int refractionCornerRadiusPixelsLocation; int refractionStrengthLocation; int refractionNormalPowLocation; int refractionRGBFringingLocation; int refractionTextureRepeatModeLocation; + int refractionModeLocation; } m_upsamplePass; struct diff --git a/src/blur.kcfg b/src/blur.kcfg index 7c52203bd..2dc1ea72e 100644 --- a/src/blur.kcfg +++ b/src/blur.kcfg @@ -91,11 +91,17 @@ class3 20.0 + + 8.0 + 1.0 0 + + 0 + diff --git a/src/kcm/blur_config.cpp b/src/kcm/blur_config.cpp index d50254ff4..cabef7893 100644 --- a/src/kcm/blur_config.cpp +++ b/src/kcm/blur_config.cpp @@ -15,6 +15,7 @@ #include #include +#include namespace KWin { @@ -28,6 +29,28 @@ BlurEffectConfig::BlurEffectConfig(QObject *parent, const KPluginMetaData &data) BlurConfig::instance("kwinrc"); addConfig(BlurConfig::self(), widget()); + // Disable Edge Behavior when Concave mode is selected - not relevant + auto updateEdgeBehaviorEnabled = [this]() { + const bool concave = ui.kcfg_RefractionMode && ui.kcfg_RefractionMode->currentIndex() == 1; + if (ui.kcfg_RefractionTextureRepeatMode) { + ui.kcfg_RefractionTextureRepeatMode->setEnabled(!concave); + } + if (ui.labelRefractionTextureRepeatMode) { + ui.labelRefractionTextureRepeatMode->setEnabled(!concave); + } + // Corner radius is only relevant for Concave as Basic breaks with low values + if (ui.kcfg_RefractionCornerRadius) { + ui.kcfg_RefractionCornerRadius->setEnabled(concave); + } + if (ui.labelRefractionCornerRadius) { + ui.labelRefractionCornerRadius->setEnabled(concave); + } + }; + if (ui.kcfg_RefractionMode) { + connect(ui.kcfg_RefractionMode, QOverload::of(&QComboBox::currentIndexChanged), this, [updateEdgeBehaviorEnabled](int){ updateEdgeBehaviorEnabled(); }); + updateEdgeBehaviorEnabled(); + } + connect(ui.staticBlurImagePicker, &QPushButton::clicked, this, &BlurEffectConfig::slotStaticBlurImagePickerClicked); QFile about(":/effects/forceblur/kcm/about.html"); diff --git a/src/kcm/blur_config.ui b/src/kcm/blur_config.ui index 2d806bd08..787819a1b 100644 --- a/src/kcm/blur_config.ui +++ b/src/kcm/blur_config.ui @@ -402,7 +402,7 @@ 0 - 20 + 30 1 @@ -431,6 +431,47 @@ + + + Refraction Mode: + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + Basic (Bulge) + + + + + Concave (Lens) + + + + + + + Refraction Edge Size: @@ -468,7 +509,7 @@ 0 - 20 + 30 1 @@ -563,16 +604,16 @@ - + - RGB Fringing Strength: + Refraction Corner Radius: - + - + Qt::Horizontal @@ -588,28 +629,25 @@ - + - Off + Square - + 0 - 20 + 200 1 - 1 - - - 10 + 7 Qt::Horizontal @@ -617,26 +655,29 @@ QSlider::TicksBelow + + 7 + - + - Strong + Round - + - Edge Behavior: + RGB Fringing Strength: - + @@ -654,21 +695,90 @@ - - - - Clamp (extend edge pixels) - - - - - Flip (mirror texture) - - + + + Off + + + + + + + 0 + + + 30 + + + 1 + + + 1 + + + 10 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + + + + + Strong + + + + + Edge Behavior: + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + true + + + + Clamp (extend edge pixels) + + + + + Flip (mirror texture) + + + + + + diff --git a/src/settings.cpp b/src/settings.cpp index ae3db6001..37c0aa98c 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -66,10 +66,21 @@ void BlurSettings::read() staticBlur.blurCustomImage = BlurConfig::fakeBlurCustomImageBlur(); refraction.edgeSizePixels = BlurConfig::refractionEdgeSize() * 10; - refraction.refractionStrength = BlurConfig::refractionStrength() / 20.0; + + { + const double maxCorner = 200.0; + const double steps = 30.0; + const double stepSize = maxCorner / steps; // ≈6.6667 + const double raw = BlurConfig::refractionCornerRadius(); + const double snapped = std::round(raw / stepSize) * stepSize; + refraction.refractionCornerRadiusPixels = snapped; + } + + refraction.refractionStrength = BlurConfig::refractionStrength() / 30.0; refraction.refractionNormalPow = BlurConfig::refractionNormalPow() / 2.0; - refraction.refractionRGBFringing = BlurConfig::refractionRGBFringing() / 20.0; // Scale to 0-1 range + refraction.refractionRGBFringing = BlurConfig::refractionRGBFringing() / 30.0; refraction.refractionTextureRepeatMode = BlurConfig::refractionTextureRepeatMode(); + refraction.refractionMode = BlurConfig::refractionMode(); } } \ No newline at end of file diff --git a/src/settings.h b/src/settings.h index 33cf06eae..37e84bbe2 100644 --- a/src/settings.h +++ b/src/settings.h @@ -61,10 +61,12 @@ struct StaticBlurSettings struct RefractionSettings { float edgeSizePixels; + float refractionCornerRadiusPixels; float refractionStrength; float refractionNormalPow; float refractionRGBFringing; int refractionTextureRepeatMode; + int refractionMode; // 0: Basic (bulge), 1: Concave (lens) }; class BlurSettings diff --git a/src/shaders/texture.frag b/src/shaders/texture.frag new file mode 100644 index 000000000..28fcc67a2 --- /dev/null +++ b/src/shaders/texture.frag @@ -0,0 +1,43 @@ +uniform float topCornerRadius; +uniform float bottomCornerRadius; +uniform float antialiasing; + +uniform vec2 blurSize; +uniform float opacity; + +vec4 roundedRectangle(vec2 fragCoord, vec3 texture) +{ + if (topCornerRadius == 0 && bottomCornerRadius == 0) { + return vec4(texture, opacity); + } + + vec2 halfblurSize = blurSize * 0.5; + vec2 p = fragCoord - halfblurSize; + float radius = 0.0; + if ((fragCoord.y <= bottomCornerRadius) + && (fragCoord.x <= bottomCornerRadius || fragCoord.x >= blurSize.x - bottomCornerRadius)) { + radius = bottomCornerRadius; + p.y -= radius; + } else if ((fragCoord.y >= blurSize.y - topCornerRadius) + && (fragCoord.x <= topCornerRadius || fragCoord.x >= blurSize.x - topCornerRadius)) { + radius = topCornerRadius; + p.y += radius; + } + float distance = length(max(abs(p) - (halfblurSize + vec2(0.0, radius)) + radius, 0.0)) - radius; + + float s = smoothstep(0.0, antialiasing, distance); + return vec4(texture, mix(1.0, 0.0, s) * opacity); +} + + +uniform sampler2D texUnit; +uniform vec2 textureSize; +uniform vec2 texStartPos; + +varying vec2 uv; + +void main(void) +{ + vec2 tex = (texStartPos.xy + vec2(uv.x, 1.0 - uv.y) * blurSize) / textureSize; + gl_FragColor = roundedRectangle(uv * blurSize, texture2D(texUnit, tex).rgb); +} \ No newline at end of file diff --git a/src/shaders/texture_core.frag b/src/shaders/texture_core.frag new file mode 100644 index 000000000..6ec76f7be --- /dev/null +++ b/src/shaders/texture_core.frag @@ -0,0 +1,47 @@ +#version 140 + +uniform float topCornerRadius; +uniform float bottomCornerRadius; +uniform float antialiasing; + +uniform vec2 blurSize; +uniform float opacity; + +vec4 roundedRectangle(vec2 fragCoord, vec3 texture) +{ + if (topCornerRadius == 0 && bottomCornerRadius == 0) { + return vec4(texture, opacity); + } + + vec2 halfblurSize = blurSize * 0.5; + vec2 p = fragCoord - halfblurSize; + float radius = 0.0; + if ((fragCoord.y <= bottomCornerRadius) + && (fragCoord.x <= bottomCornerRadius || fragCoord.x >= blurSize.x - bottomCornerRadius)) { + radius = bottomCornerRadius; + p.y -= radius; + } else if ((fragCoord.y >= blurSize.y - topCornerRadius) + && (fragCoord.x <= topCornerRadius || fragCoord.x >= blurSize.x - topCornerRadius)) { + radius = topCornerRadius; + p.y += radius; + } + float distance = length(max(abs(p) - (halfblurSize + vec2(0.0, radius)) + radius, 0.0)) - radius; + + float s = smoothstep(0.0, antialiasing, distance); + return vec4(texture, mix(1.0, 0.0, s) * opacity); +} + + +uniform sampler2D texUnit; +uniform vec2 textureSize; +uniform vec2 texStartPos; + +in vec2 uv; + +out vec4 fragColor; + +void main(void) +{ + vec2 tex = (texStartPos.xy + vec2(uv.x, 1.0 - uv.y) * blurSize) / textureSize; + fragColor = roundedRectangle(uv * blurSize, texture(texUnit, tex).rgb); +} diff --git a/src/shaders/upsample.frag b/src/shaders/upsample.frag new file mode 100644 index 000000000..4cea5bb03 --- /dev/null +++ b/src/shaders/upsample.frag @@ -0,0 +1,204 @@ +uniform float topCornerRadius; +uniform float bottomCornerRadius; +uniform float antialiasing; + +uniform vec2 blurSize; +uniform float opacity; + +vec4 roundedRectangle(vec2 fragCoord, vec3 texture) +{ + if (topCornerRadius == 0 && bottomCornerRadius == 0) { + return vec4(texture, opacity); + } + + vec2 halfblurSize = blurSize * 0.5; + vec2 p = fragCoord - halfblurSize; + float radius = 0.0; + if ((fragCoord.y <= bottomCornerRadius) + && (fragCoord.x <= bottomCornerRadius || fragCoord.x >= blurSize.x - bottomCornerRadius)) { + radius = bottomCornerRadius; + p.y -= radius; + } else if ((fragCoord.y >= blurSize.y - topCornerRadius) + && (fragCoord.x <= topCornerRadius || fragCoord.x >= blurSize.x - topCornerRadius)) { + radius = topCornerRadius; + p.y += radius; + } + float distance = length(max(abs(p) - (halfblurSize + vec2(0.0, radius)) + radius, 0.0)) - radius; + + float s = smoothstep(0.0, antialiasing, distance); + return vec4(texture, mix(1.0, 0.0, s) * opacity); +} + + +uniform sampler2D texUnit; +uniform float offset; +uniform vec2 halfpixel; + +uniform bool noise; +uniform sampler2D noiseTexture; +uniform vec2 noiseTextureSize; + +uniform float edgeSizePixels; +uniform float refractionCornerRadiusPixels; +uniform float refractionStrength; +uniform float refractionNormalPow; +uniform float refractionRGBFringing; +uniform int refractionTextureRepeatMode; +uniform int refractionMode; // 0: Basic, 1: Concave + +varying vec2 uv; + +vec2 applyTextureRepeatMode(vec2 coord) +{ + if (refractionTextureRepeatMode == 0) { + return clamp(coord, 0.0, 1.0); + } else if (refractionTextureRepeatMode == 1) { + // flip on both axes + vec2 flip = mod(coord, 2.0); + + vec2 result = coord; + if (flip.x > 1.0) { + result.x = 1.0 - mod(coord.x, 1.0); + } else { + result.x = mod(coord.x, 1.0); + } + + if (flip.y > 1.0) { + result.y = 1.0 - mod(coord.y, 1.0); + } else { + result.y = mod(coord.y, 1.0); + } + + return result; + } + return coord; +} + +// Concave lens-style radial mapping around the rect center, shaped by distance to edge +vec2 concaveLensCoord(vec2 uv, float strength, float fringing, float dist, vec2 halfBlurSize) +{ + // Edge proximity: 0 in the deep interior, 1 near the rounded rectangle edge + float edgeProximity = clamp(1.0 + dist / edgeSizePixels, 0.0, 1.0); + float shaped = sin(pow(edgeProximity, refractionNormalPow) * 1.57079632679); + + vec2 fromCenter = uv - vec2(0.5); + + float scaleR = 1.0 - shaped * strength * (1.0 + fringing); + float scaleG = 1.0 - shaped * strength; + float scaleB = 1.0 - shaped * strength * (1.0 - fringing); + + // Return per-channel lens coords packed in vec2 three times via caller + // Caller samples each channel separately with the right scale + // Here we just return the green channel scale as a convenience; R and B will be built in caller + return vec2(0.5) + fromCenter * scaleG; +} + +// source: https://iquilezles.org/articles/distfunctions2d/ +// https://www.shadertoy.com/view/4llXD7 +float roundedRectangleDist(vec2 p, vec2 b, float r) +{ + vec2 q = abs(p) - b + r; + return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r; +} + +void main(void) +{ + vec2 offsets[8] = vec2[]( + vec2(-halfpixel.x * 2.0, 0.0), + vec2(-halfpixel.x, halfpixel.y), + vec2(0.0, halfpixel.y * 2.0), + vec2(halfpixel.x, halfpixel.y), + vec2(halfpixel.x * 2.0, 0.0), + vec2(halfpixel.x, -halfpixel.y), + vec2(0.0, -halfpixel.y * 2.0), + vec2(-halfpixel.x, -halfpixel.y) + ); + float weights[8] = float[](1.0, 2.0, 1.0, 2.0, 1.0, 2.0, 1.0, 2.0); + float weightSum = 12.0; + vec4 sum = vec4(0, 0, 0, 0); + + if (refractionStrength > 0) { + vec2 halfBlurSize = 0.5 * blurSize; + vec2 position = uv * blurSize - halfBlurSize.xy; + float cornerR = min(refractionCornerRadiusPixels, min(halfBlurSize.x, halfBlurSize.y)); + float distConcave = roundedRectangleDist(position, halfBlurSize, cornerR); + float distBulge = roundedRectangleDist(position, halfBlurSize, edgeSizePixels); + + // Different refraction behavior depending on mode + if (refractionMode == 1) { + // Concave: lens-like radial mapping with RGB fringing + float fringing = refractionRGBFringing * 0.3; + float baseStrength = 0.2 * refractionStrength; + + // Edge proximity shaping + float edgeProximity = clamp(1.0 + distConcave / edgeSizePixels, 0.0, 1.0); + float shaped = sin(pow(edgeProximity, refractionNormalPow) * 1.57079632679); + + vec2 fromCenter = uv - vec2(0.5); + float scaleR = 1.0 - shaped * baseStrength * (1.0 + fringing); + float scaleG = 1.0 - shaped * baseStrength; + float scaleB = 1.0 - shaped * baseStrength * (1.0 - fringing); + + vec2 coordR = applyTextureRepeatMode(vec2(0.5) + fromCenter * scaleR); + vec2 coordG = applyTextureRepeatMode(vec2(0.5) + fromCenter * scaleG); + vec2 coordB = applyTextureRepeatMode(vec2(0.5) + fromCenter * scaleB); + + for (int i = 0; i < 8; ++i) { + vec2 off = offsets[i] * offset; + sum.r += texture2D(texUnit, coordR + off).r * weights[i]; + sum.g += texture2D(texUnit, coordG + off).g * weights[i]; + sum.b += texture2D(texUnit, coordB + off).b * weights[i]; + sum.a += texture2D(texUnit, coordG + off).a * weights[i]; + } + + sum /= weightSum; + } else { + // Basic: convex/bulge-like along inward normal from the rounded-rect edge + float concaveFactor = pow(clamp(1.0 + distBulge / edgeSizePixels, 0.0, 1.0), refractionNormalPow); + + // Initial 2D normal + const float h = 1.0; + vec2 gradient = vec2( + roundedRectangleDist(position + vec2(h, 0), halfBlurSize, edgeSizePixels) - roundedRectangleDist(position - vec2(h, 0), halfBlurSize, edgeSizePixels), + roundedRectangleDist(position + vec2(0, h), halfBlurSize, edgeSizePixels) - roundedRectangleDist(position - vec2(0, h), halfBlurSize, edgeSizePixels) + ); + + vec2 normal = length(gradient) > 1e-6 ? -normalize(gradient) : vec2(0.0, 1.0); + + float finalStrength = 0.2 * concaveFactor * refractionStrength; + + // Different refraction offsets for each color channel + float fringingFactor = refractionRGBFringing * 0.3; + vec2 refractOffsetR = normal.xy * (finalStrength * (1.0 + fringingFactor)); // Red bends most + vec2 refractOffsetG = normal.xy * finalStrength; + vec2 refractOffsetB = normal.xy * (finalStrength * (1.0 - fringingFactor)); // Blue bends least + + vec2 coordR = applyTextureRepeatMode(uv - refractOffsetR); + vec2 coordG = applyTextureRepeatMode(uv - refractOffsetG); + vec2 coordB = applyTextureRepeatMode(uv - refractOffsetB); + + for (int i = 0; i < 8; ++i) { + vec2 off = offsets[i] * offset; + sum.r += texture2D(texUnit, coordR + off).r * weights[i]; + sum.g += texture2D(texUnit, coordG + off).g * weights[i]; + sum.b += texture2D(texUnit, coordB + off).b * weights[i]; + sum.a += texture2D(texUnit, coordG + off).a * weights[i]; + } + + sum /= weightSum; + } + } else { + for (int i = 0; i < 8; ++i) { + vec2 off = offsets[i] * offset; + sum += texture2D(texUnit, uv + off) * weights[i]; + } + + sum /= weightSum; + } + + if (noise) { + sum += vec4(texture2D(noiseTexture, vec2(uv.x, 1.0 - uv.y) * blurSize / noiseTextureSize).rrr, 0.0); + } + + gl_FragColor = roundedRectangle(uv * blurSize, sum.rgb); +} \ No newline at end of file diff --git a/src/shaders/upsample.glsl b/src/shaders/upsample.glsl index 713566a94..048ec89ea 100644 --- a/src/shaders/upsample.glsl +++ b/src/shaders/upsample.glsl @@ -9,10 +9,12 @@ uniform sampler2D noiseTexture; uniform vec2 noiseTextureSize; uniform float edgeSizePixels; +uniform float refractionCornerRadiusPixels; uniform float refractionStrength; uniform float refractionNormalPow; uniform float refractionRGBFringing; uniform int refractionTextureRepeatMode; +uniform int refractionMode; // 0: Basic, 1: Concave varying vec2 uv; @@ -42,6 +44,25 @@ vec2 applyTextureRepeatMode(vec2 coord) return coord; } +// Concave lens-style radial mapping around the rect center, shaped by distance to edge +vec2 concaveLensCoord(vec2 uv, float strength, float fringing, float dist, vec2 halfBlurSize) +{ + // Edge proximity: 0 in the deep interior, 1 near the rounded rectangle edge + float edgeProximity = clamp(1.0 + dist / edgeSizePixels, 0.0, 1.0); + float shaped = sin(pow(edgeProximity, refractionNormalPow) * 1.57079632679); + + vec2 fromCenter = uv - vec2(0.5); + + float scaleR = 1.0 - shaped * strength * (1.0 + fringing); + float scaleG = 1.0 - shaped * strength; + float scaleB = 1.0 - shaped * strength * (1.0 - fringing); + + // Return per-channel lens coords packed in vec2 three times via caller + // Caller samples each channel separately with the right scale + // Here we just return the green channel scale as a convenience; R and B will be built in caller + return vec2(0.5) + fromCenter * scaleG; +} + // source: https://iquilezles.org/articles/distfunctions2d/ // https://www.shadertoy.com/view/4llXD7 float roundedRectangleDist(vec2 p, vec2 b, float r) @@ -69,40 +90,73 @@ void main(void) if (refractionStrength > 0) { vec2 halfBlurSize = 0.5 * blurSize; vec2 position = uv * blurSize - halfBlurSize.xy; - float dist = roundedRectangleDist(position, halfBlurSize, edgeSizePixels); - - float concaveFactor = pow(clamp(1.0 + dist / edgeSizePixels, 0.0, 1.0), refractionNormalPow); - - // Initial 2D normal - const float h = 1.0; - vec2 gradient = vec2( - roundedRectangleDist(position + vec2(h, 0), halfBlurSize, edgeSizePixels) - roundedRectangleDist(position - vec2(h, 0), halfBlurSize, edgeSizePixels), - roundedRectangleDist(position + vec2(0, h), halfBlurSize, edgeSizePixels) - roundedRectangleDist(position - vec2(0, h), halfBlurSize, edgeSizePixels) - ); - - vec2 normal = length(gradient) > 1e-6 ? -normalize(gradient) : vec2(0.0, 1.0); - - float finalStrength = 0.2 * concaveFactor * refractionStrength; - - // Different refraction offsets for each color channel - float fringingFactor = refractionRGBFringing * 0.3; - vec2 refractOffsetR = normal.xy * (finalStrength * (1.0 + fringingFactor)); // Red bends most - vec2 refractOffsetG = normal.xy * finalStrength; - vec2 refractOffsetB = normal.xy * (finalStrength * (1.0 - fringingFactor)); // Blue bends least - - vec2 coordR = applyTextureRepeatMode(uv - refractOffsetR); - vec2 coordG = applyTextureRepeatMode(uv - refractOffsetG); - vec2 coordB = applyTextureRepeatMode(uv - refractOffsetB); - - for (int i = 0; i < 8; ++i) { - vec2 off = offsets[i] * offset; - sum.r += texture2D(texUnit, coordR + off).r * weights[i]; - sum.g += texture2D(texUnit, coordG + off).g * weights[i]; - sum.b += texture2D(texUnit, coordB + off).b * weights[i]; - sum.a += texture2D(texUnit, coordG + off).a * weights[i]; + float cornerR = min(refractionCornerRadiusPixels, min(halfBlurSize.x, halfBlurSize.y)); + float distConcave = roundedRectangleDist(position, halfBlurSize, cornerR); + float distBulge = roundedRectangleDist(position, halfBlurSize, edgeSizePixels); + + // Different refraction behavior depending on mode + if (refractionMode == 1) { + // Concave: lens-like radial mapping with RGB fringing + float fringing = refractionRGBFringing * 0.3; + float baseStrength = 0.2 * refractionStrength; + + // Edge proximity shaping + float edgeProximity = clamp(1.0 + distConcave / edgeSizePixels, 0.0, 1.0); + float shaped = sin(pow(edgeProximity, refractionNormalPow) * 1.57079632679); + + vec2 fromCenter = uv - vec2(0.5); + float scaleR = 1.0 - shaped * baseStrength * (1.0 + fringing); + float scaleG = 1.0 - shaped * baseStrength; + float scaleB = 1.0 - shaped * baseStrength * (1.0 - fringing); + + vec2 coordR = applyTextureRepeatMode(vec2(0.5) + fromCenter * scaleR); + vec2 coordG = applyTextureRepeatMode(vec2(0.5) + fromCenter * scaleG); + vec2 coordB = applyTextureRepeatMode(vec2(0.5) + fromCenter * scaleB); + + for (int i = 0; i < 8; ++i) { + vec2 off = offsets[i] * offset; + sum.r += texture2D(texUnit, coordR + off).r * weights[i]; + sum.g += texture2D(texUnit, coordG + off).g * weights[i]; + sum.b += texture2D(texUnit, coordB + off).b * weights[i]; + sum.a += texture2D(texUnit, coordG + off).a * weights[i]; + } + + sum /= weightSum; + } else { + // Basic: convex/bulge-like along inward normal from the rounded-rect edge + float concaveFactor = pow(clamp(1.0 + distBulge / edgeSizePixels, 0.0, 1.0), refractionNormalPow); + + // Initial 2D normal + const float h = 1.0; + vec2 gradient = vec2( + roundedRectangleDist(position + vec2(h, 0), halfBlurSize, edgeSizePixels) - roundedRectangleDist(position - vec2(h, 0), halfBlurSize, edgeSizePixels), + roundedRectangleDist(position + vec2(0, h), halfBlurSize, edgeSizePixels) - roundedRectangleDist(position - vec2(0, h), halfBlurSize, edgeSizePixels) + ); + + vec2 normal = length(gradient) > 1e-6 ? -normalize(gradient) : vec2(0.0, 1.0); + + float finalStrength = 0.2 * concaveFactor * refractionStrength; + + // Different refraction offsets for each color channel + float fringingFactor = refractionRGBFringing * 0.3; + vec2 refractOffsetR = normal.xy * (finalStrength * (1.0 + fringingFactor)); // Red bends most + vec2 refractOffsetG = normal.xy * finalStrength; + vec2 refractOffsetB = normal.xy * (finalStrength * (1.0 - fringingFactor)); // Blue bends least + + vec2 coordR = applyTextureRepeatMode(uv - refractOffsetR); + vec2 coordG = applyTextureRepeatMode(uv - refractOffsetG); + vec2 coordB = applyTextureRepeatMode(uv - refractOffsetB); + + for (int i = 0; i < 8; ++i) { + vec2 off = offsets[i] * offset; + sum.r += texture2D(texUnit, coordR + off).r * weights[i]; + sum.g += texture2D(texUnit, coordG + off).g * weights[i]; + sum.b += texture2D(texUnit, coordB + off).b * weights[i]; + sum.a += texture2D(texUnit, coordG + off).a * weights[i]; + } + + sum /= weightSum; } - - sum /= weightSum; } else { for (int i = 0; i < 8; ++i) { vec2 off = offsets[i] * offset; diff --git a/src/shaders/upsample_core.frag b/src/shaders/upsample_core.frag new file mode 100644 index 000000000..80f5ba2ea --- /dev/null +++ b/src/shaders/upsample_core.frag @@ -0,0 +1,198 @@ +#version 140 + +uniform float topCornerRadius; +uniform float bottomCornerRadius; +uniform float antialiasing; + +uniform vec2 blurSize; +uniform float opacity; + +vec4 roundedRectangle(vec2 fragCoord, vec3 texture) +{ + if (topCornerRadius == 0 && bottomCornerRadius == 0) { + return vec4(texture, opacity); + } + + vec2 halfblurSize = blurSize * 0.5; + vec2 p = fragCoord - halfblurSize; + float radius = 0.0; + if ((fragCoord.y <= bottomCornerRadius) + && (fragCoord.x <= bottomCornerRadius || fragCoord.x >= blurSize.x - bottomCornerRadius)) { + radius = bottomCornerRadius; + p.y -= radius; + } else if ((fragCoord.y >= blurSize.y - topCornerRadius) + && (fragCoord.x <= topCornerRadius || fragCoord.x >= blurSize.x - topCornerRadius)) { + radius = topCornerRadius; + p.y += radius; + } + float distance = length(max(abs(p) - (halfblurSize + vec2(0.0, radius)) + radius, 0.0)) - radius; + + float s = smoothstep(0.0, antialiasing, distance); + return vec4(texture, mix(1.0, 0.0, s) * opacity); +} + + +uniform sampler2D texUnit; +uniform float offset; +uniform vec2 halfpixel; + +uniform bool noise; +uniform sampler2D noiseTexture; +uniform vec2 noiseTextureSize; + +uniform float edgeSizePixels; +uniform float refractionCornerRadiusPixels; +uniform float refractionStrength; +uniform float refractionNormalPow; +uniform float refractionRGBFringing; +uniform int refractionTextureRepeatMode; +uniform int refractionMode; // 0: Basic, 1: Concave + +in vec2 uv; +out vec4 fragColor; + +vec2 applyTextureRepeatMode(vec2 coord) +{ + if (refractionTextureRepeatMode == 0) { + return clamp(coord, 0.0, 1.0); + } else if (refractionTextureRepeatMode == 1) { + // flip on both axes + vec2 flip = mod(coord, 2.0); + + vec2 result = coord; + if (flip.x > 1.0) { + result.x = 1.0 - mod(coord.x, 1.0); + } else { + result.x = mod(coord.x, 1.0); + } + + if (flip.y > 1.0) { + result.y = 1.0 - mod(coord.y, 1.0); + } else { + result.y = mod(coord.y, 1.0); + } + + return result; + } + return coord; +} + +// Concave lens-style radial mapping around the rect center, shaped by distance to edge +vec2 concaveLensCoord(vec2 uv, float strength, float fringing, float dist, vec2 halfBlurSize) +{ + float edgeProximity = clamp(1.0 + dist / edgeSizePixels, 0.0, 1.0); + float shaped = sin(pow(edgeProximity, refractionNormalPow) * 1.57079632679); + + vec2 fromCenter = uv - vec2(0.5); + + float scaleR = 1.0 - shaped * strength * (1.0 + fringing); + float scaleG = 1.0 - shaped * strength; + float scaleB = 1.0 - shaped * strength * (1.0 - fringing); + + return vec2(0.5) + fromCenter * scaleG; +} + +// source: https://iquilezles.org/articles/distfunctions2d/ +// https://www.shadertoy.com/view/4llXD7 +float roundedRectangleDist(vec2 p, vec2 b, float r) +{ + vec2 q = abs(p) - b + r; + return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r; +} + +void main(void) +{ + vec2 offsets[8] = vec2[]( + vec2(-halfpixel.x * 2.0, 0.0), + vec2(-halfpixel.x, halfpixel.y), + vec2(0.0, halfpixel.y * 2.0), + vec2(halfpixel.x, halfpixel.y), + vec2(halfpixel.x * 2.0, 0.0), + vec2(halfpixel.x, -halfpixel.y), + vec2(0.0, -halfpixel.y * 2.0), + vec2(-halfpixel.x, -halfpixel.y) + ); + float weights[8] = float[](1.0, 2.0, 1.0, 2.0, 1.0, 2.0, 1.0, 2.0); + float weightSum = 12.0; + vec4 sum = vec4(0, 0, 0, 0); + + if (refractionStrength > 0) { + vec2 halfBlurSize = 0.5 * blurSize; + vec2 position = uv * blurSize - halfBlurSize.xy; + float cornerR = min(refractionCornerRadiusPixels, min(halfBlurSize.x, halfBlurSize.y)); + float distConcave = roundedRectangleDist(position, halfBlurSize, cornerR); + float distBulge = roundedRectangleDist(position, halfBlurSize, edgeSizePixels); + if (refractionMode == 1) { + float fringing = refractionRGBFringing * 0.3; + float baseStrength = 0.2 * refractionStrength; + + float edgeProximity = clamp(1.0 + distConcave / edgeSizePixels, 0.0, 1.0); + float shaped = sin(pow(edgeProximity, refractionNormalPow) * 1.57079632679); + + vec2 fromCenter = uv - vec2(0.5); + float scaleR = 1.0 - shaped * baseStrength * (1.0 + fringing); + float scaleG = 1.0 - shaped * baseStrength; + float scaleB = 1.0 - shaped * baseStrength * (1.0 - fringing); + + vec2 coordR = applyTextureRepeatMode(vec2(0.5) + fromCenter * scaleR); + vec2 coordG = applyTextureRepeatMode(vec2(0.5) + fromCenter * scaleG); + vec2 coordB = applyTextureRepeatMode(vec2(0.5) + fromCenter * scaleB); + + for (int i = 0; i < 8; ++i) { + vec2 off = offsets[i] * offset; + sum.r += texture(texUnit, coordR + off).r * weights[i]; + sum.g += texture(texUnit, coordG + off).g * weights[i]; + sum.b += texture(texUnit, coordB + off).b * weights[i]; + sum.a += texture(texUnit, coordG + off).a * weights[i]; + } + + sum /= weightSum; + } else { + float concaveFactor = pow(clamp(1.0 + distBulge / edgeSizePixels, 0.0, 1.0), refractionNormalPow); + + // Initial 2D normal + const float h = 1.0; + vec2 gradient = vec2( + roundedRectangleDist(position + vec2(h, 0), halfBlurSize, edgeSizePixels) - roundedRectangleDist(position - vec2(h, 0), halfBlurSize, edgeSizePixels), + roundedRectangleDist(position + vec2(0, h), halfBlurSize, edgeSizePixels) - roundedRectangleDist(position - vec2(0, h), halfBlurSize, edgeSizePixels) + ); + + vec2 normal = length(gradient) > 1e-6 ? -normalize(gradient) : vec2(0.0, 1.0); + + float finalStrength = 0.2 * concaveFactor * refractionStrength; + + // Different refraction offsets for each color channel + float fringingFactor = refractionRGBFringing * 0.3; + vec2 refractOffsetR = normal.xy * (finalStrength * (1.0 + fringingFactor)); // Red bends most + vec2 refractOffsetG = normal.xy * finalStrength; + vec2 refractOffsetB = normal.xy * (finalStrength * (1.0 - fringingFactor)); // Blue bends least + + vec2 coordR = applyTextureRepeatMode(uv - refractOffsetR); + vec2 coordG = applyTextureRepeatMode(uv - refractOffsetG); + vec2 coordB = applyTextureRepeatMode(uv - refractOffsetB); + + for (int i = 0; i < 8; ++i) { + vec2 off = offsets[i] * offset; + sum.r += texture(texUnit, coordR + off).r * weights[i]; + sum.g += texture(texUnit, coordG + off).g * weights[i]; + sum.b += texture(texUnit, coordB + off).b * weights[i]; + sum.a += texture(texUnit, coordG + off).a * weights[i]; + } + + sum /= weightSum; + } + } else { + for (int i = 0; i < 8; ++i) { + vec2 off = offsets[i] * offset; + sum += texture(texUnit, uv + off) * weights[i]; + } + + sum /= weightSum; + } + + if (noise) { + sum += vec4(texture(noiseTexture, vec2(uv.x, 1.0 - uv.y) * blurSize / noiseTextureSize).rrr, 0.0); + } + + fragColor = roundedRectangle(uv * blurSize, sum.rgb); +} \ No newline at end of file diff --git a/src/shaders/upsample_core.glsl b/src/shaders/upsample_core.glsl index 6f6dd5206..7b55e00b6 100644 --- a/src/shaders/upsample_core.glsl +++ b/src/shaders/upsample_core.glsl @@ -11,10 +11,12 @@ uniform sampler2D noiseTexture; uniform vec2 noiseTextureSize; uniform float edgeSizePixels; +uniform float refractionCornerRadiusPixels; uniform float refractionStrength; uniform float refractionNormalPow; uniform float refractionRGBFringing; uniform int refractionTextureRepeatMode; +uniform int refractionMode; // 0: Basic, 1: Concave in vec2 uv; out vec4 fragColor; @@ -45,6 +47,21 @@ vec2 applyTextureRepeatMode(vec2 coord) return coord; } +// Concave lens-style radial mapping around the rect center, shaped by distance to edge +vec2 concaveLensCoord(vec2 uv, float strength, float fringing, float dist, vec2 halfBlurSize) +{ + float edgeProximity = clamp(1.0 + dist / edgeSizePixels, 0.0, 1.0); + float shaped = sin(pow(edgeProximity, refractionNormalPow) * 1.57079632679); + + vec2 fromCenter = uv - vec2(0.5); + + float scaleR = 1.0 - shaped * strength * (1.0 + fringing); + float scaleG = 1.0 - shaped * strength; + float scaleB = 1.0 - shaped * strength * (1.0 - fringing); + + return vec2(0.5) + fromCenter * scaleG; +} + // source: https://iquilezles.org/articles/distfunctions2d/ // https://www.shadertoy.com/view/4llXD7 float roundedRectangleDist(vec2 p, vec2 b, float r) @@ -72,40 +89,68 @@ void main(void) if (refractionStrength > 0) { vec2 halfBlurSize = 0.5 * blurSize; vec2 position = uv * blurSize - halfBlurSize.xy; - float dist = roundedRectangleDist(position, halfBlurSize, edgeSizePixels); - - float concaveFactor = pow(clamp(1.0 + dist / edgeSizePixels, 0.0, 1.0), refractionNormalPow); - - // Initial 2D normal - const float h = 1.0; - vec2 gradient = vec2( - roundedRectangleDist(position + vec2(h, 0), halfBlurSize, edgeSizePixels) - roundedRectangleDist(position - vec2(h, 0), halfBlurSize, edgeSizePixels), - roundedRectangleDist(position + vec2(0, h), halfBlurSize, edgeSizePixels) - roundedRectangleDist(position - vec2(0, h), halfBlurSize, edgeSizePixels) - ); - - vec2 normal = length(gradient) > 1e-6 ? -normalize(gradient) : vec2(0.0, 1.0); - - float finalStrength = 0.2 * concaveFactor * refractionStrength; - - // Different refraction offsets for each color channel - float fringingFactor = refractionRGBFringing * 0.3; - vec2 refractOffsetR = normal.xy * (finalStrength * (1.0 + fringingFactor)); // Red bends most - vec2 refractOffsetG = normal.xy * finalStrength; - vec2 refractOffsetB = normal.xy * (finalStrength * (1.0 - fringingFactor)); // Blue bends least - - vec2 coordR = applyTextureRepeatMode(uv - refractOffsetR); - vec2 coordG = applyTextureRepeatMode(uv - refractOffsetG); - vec2 coordB = applyTextureRepeatMode(uv - refractOffsetB); - - for (int i = 0; i < 8; ++i) { - vec2 off = offsets[i] * offset; - sum.r += texture(texUnit, coordR + off).r * weights[i]; - sum.g += texture(texUnit, coordG + off).g * weights[i]; - sum.b += texture(texUnit, coordB + off).b * weights[i]; - sum.a += texture(texUnit, coordG + off).a * weights[i]; + float cornerR = min(refractionCornerRadiusPixels, min(halfBlurSize.x, halfBlurSize.y)); + float distConcave = roundedRectangleDist(position, halfBlurSize, cornerR); + float distBulge = roundedRectangleDist(position, halfBlurSize, edgeSizePixels); + if (refractionMode == 1) { + float fringing = refractionRGBFringing * 0.3; + float baseStrength = 0.2 * refractionStrength; + + float edgeProximity = clamp(1.0 + distConcave / edgeSizePixels, 0.0, 1.0); + float shaped = sin(pow(edgeProximity, refractionNormalPow) * 1.57079632679); + + vec2 fromCenter = uv - vec2(0.5); + float scaleR = 1.0 - shaped * baseStrength * (1.0 + fringing); + float scaleG = 1.0 - shaped * baseStrength; + float scaleB = 1.0 - shaped * baseStrength * (1.0 - fringing); + + vec2 coordR = applyTextureRepeatMode(vec2(0.5) + fromCenter * scaleR); + vec2 coordG = applyTextureRepeatMode(vec2(0.5) + fromCenter * scaleG); + vec2 coordB = applyTextureRepeatMode(vec2(0.5) + fromCenter * scaleB); + + for (int i = 0; i < 8; ++i) { + vec2 off = offsets[i] * offset; + sum.r += texture(texUnit, coordR + off).r * weights[i]; + sum.g += texture(texUnit, coordG + off).g * weights[i]; + sum.b += texture(texUnit, coordB + off).b * weights[i]; + sum.a += texture(texUnit, coordG + off).a * weights[i]; + } + + sum /= weightSum; + } else { + float concaveFactor = pow(clamp(1.0 + distBulge / edgeSizePixels, 0.0, 1.0), refractionNormalPow); + + // Initial 2D normal + const float h = 1.0; + vec2 gradient = vec2( + roundedRectangleDist(position + vec2(h, 0), halfBlurSize, edgeSizePixels) - roundedRectangleDist(position - vec2(h, 0), halfBlurSize, edgeSizePixels), + roundedRectangleDist(position + vec2(0, h), halfBlurSize, edgeSizePixels) - roundedRectangleDist(position - vec2(0, h), halfBlurSize, edgeSizePixels) + ); + + vec2 normal = length(gradient) > 1e-6 ? -normalize(gradient) : vec2(0.0, 1.0); + + float finalStrength = 0.2 * concaveFactor * refractionStrength; + + // Different refraction offsets for each color channel + float fringingFactor = refractionRGBFringing * 0.3; + vec2 refractOffsetR = normal.xy * (finalStrength * (1.0 + fringingFactor)); // Red bends most + vec2 refractOffsetG = normal.xy * finalStrength; + vec2 refractOffsetB = normal.xy * (finalStrength * (1.0 - fringingFactor)); // Blue bends least + + vec2 coordR = applyTextureRepeatMode(uv - refractOffsetR); + vec2 coordG = applyTextureRepeatMode(uv - refractOffsetG); + vec2 coordB = applyTextureRepeatMode(uv - refractOffsetB); + + for (int i = 0; i < 8; ++i) { + vec2 off = offsets[i] * offset; + sum.r += texture(texUnit, coordR + off).r * weights[i]; + sum.g += texture(texUnit, coordG + off).g * weights[i]; + sum.b += texture(texUnit, coordB + off).b * weights[i]; + sum.a += texture(texUnit, coordG + off).a * weights[i]; + } + + sum /= weightSum; } - - sum /= weightSum; } else { for (int i = 0; i < 8; ++i) { vec2 off = offsets[i] * offset; From b218838176d09b758bc1dfc514804f0fd82695c1 Mon Sep 17 00:00:00 2001 From: taj-ny Date: Tue, 12 Aug 2025 22:09:34 +0200 Subject: [PATCH 2/4] format blur_config.ui --- src/kcm/blur_config.ui | 196 ++++++++++++++++++++--------------------- 1 file changed, 98 insertions(+), 98 deletions(-) diff --git a/src/kcm/blur_config.ui b/src/kcm/blur_config.ui index 787819a1b..07dab0215 100644 --- a/src/kcm/blur_config.ui +++ b/src/kcm/blur_config.ui @@ -358,11 +358,11 @@ - - - Refraction does not work when using static blur. - - + + + Refraction does not work when using static blur. + + @@ -402,7 +402,7 @@ 0 - 30 + 30 1 @@ -431,47 +431,47 @@ - - - Refraction Mode: - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed + + + Refraction Mode: + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + Basic (Bulge) - - - 20 - 20 - + + + + Concave (Lens) - - - - - - - Basic (Bulge) - - - - - Concave (Lens) - - - - - - - + + + + + + Refraction Edge Size: @@ -509,7 +509,7 @@ 0 - 30 + 30 1 @@ -707,7 +707,7 @@ 0 - 30 + 30 1 @@ -735,61 +735,61 @@ - - - - Edge Behavior: - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 20 - 20 - + + + + Edge Behavior: + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + true + + + + Clamp (extend edge pixels) - - - - - - true + + + + Flip (mirror texture) - - - Clamp (extend edge pixels) - - - - - Flip (mirror texture) - - - - - - + + + + + - - Qt::Vertical - - - - 0 - 0 - - + + Qt::Vertical + + + + 0 + 0 + + From 055ad38b569f12de902f88a931264c80ac63b0bd Mon Sep 17 00:00:00 2001 From: taj-ny Date: Tue, 12 Aug 2025 22:10:21 +0200 Subject: [PATCH 3/4] delete generated shaders --- src/shaders/texture.frag | 43 ------- src/shaders/texture_core.frag | 47 -------- src/shaders/upsample.frag | 204 --------------------------------- src/shaders/upsample_core.frag | 198 -------------------------------- 4 files changed, 492 deletions(-) delete mode 100644 src/shaders/texture.frag delete mode 100644 src/shaders/texture_core.frag delete mode 100644 src/shaders/upsample.frag delete mode 100644 src/shaders/upsample_core.frag diff --git a/src/shaders/texture.frag b/src/shaders/texture.frag deleted file mode 100644 index 28fcc67a2..000000000 --- a/src/shaders/texture.frag +++ /dev/null @@ -1,43 +0,0 @@ -uniform float topCornerRadius; -uniform float bottomCornerRadius; -uniform float antialiasing; - -uniform vec2 blurSize; -uniform float opacity; - -vec4 roundedRectangle(vec2 fragCoord, vec3 texture) -{ - if (topCornerRadius == 0 && bottomCornerRadius == 0) { - return vec4(texture, opacity); - } - - vec2 halfblurSize = blurSize * 0.5; - vec2 p = fragCoord - halfblurSize; - float radius = 0.0; - if ((fragCoord.y <= bottomCornerRadius) - && (fragCoord.x <= bottomCornerRadius || fragCoord.x >= blurSize.x - bottomCornerRadius)) { - radius = bottomCornerRadius; - p.y -= radius; - } else if ((fragCoord.y >= blurSize.y - topCornerRadius) - && (fragCoord.x <= topCornerRadius || fragCoord.x >= blurSize.x - topCornerRadius)) { - radius = topCornerRadius; - p.y += radius; - } - float distance = length(max(abs(p) - (halfblurSize + vec2(0.0, radius)) + radius, 0.0)) - radius; - - float s = smoothstep(0.0, antialiasing, distance); - return vec4(texture, mix(1.0, 0.0, s) * opacity); -} - - -uniform sampler2D texUnit; -uniform vec2 textureSize; -uniform vec2 texStartPos; - -varying vec2 uv; - -void main(void) -{ - vec2 tex = (texStartPos.xy + vec2(uv.x, 1.0 - uv.y) * blurSize) / textureSize; - gl_FragColor = roundedRectangle(uv * blurSize, texture2D(texUnit, tex).rgb); -} \ No newline at end of file diff --git a/src/shaders/texture_core.frag b/src/shaders/texture_core.frag deleted file mode 100644 index 6ec76f7be..000000000 --- a/src/shaders/texture_core.frag +++ /dev/null @@ -1,47 +0,0 @@ -#version 140 - -uniform float topCornerRadius; -uniform float bottomCornerRadius; -uniform float antialiasing; - -uniform vec2 blurSize; -uniform float opacity; - -vec4 roundedRectangle(vec2 fragCoord, vec3 texture) -{ - if (topCornerRadius == 0 && bottomCornerRadius == 0) { - return vec4(texture, opacity); - } - - vec2 halfblurSize = blurSize * 0.5; - vec2 p = fragCoord - halfblurSize; - float radius = 0.0; - if ((fragCoord.y <= bottomCornerRadius) - && (fragCoord.x <= bottomCornerRadius || fragCoord.x >= blurSize.x - bottomCornerRadius)) { - radius = bottomCornerRadius; - p.y -= radius; - } else if ((fragCoord.y >= blurSize.y - topCornerRadius) - && (fragCoord.x <= topCornerRadius || fragCoord.x >= blurSize.x - topCornerRadius)) { - radius = topCornerRadius; - p.y += radius; - } - float distance = length(max(abs(p) - (halfblurSize + vec2(0.0, radius)) + radius, 0.0)) - radius; - - float s = smoothstep(0.0, antialiasing, distance); - return vec4(texture, mix(1.0, 0.0, s) * opacity); -} - - -uniform sampler2D texUnit; -uniform vec2 textureSize; -uniform vec2 texStartPos; - -in vec2 uv; - -out vec4 fragColor; - -void main(void) -{ - vec2 tex = (texStartPos.xy + vec2(uv.x, 1.0 - uv.y) * blurSize) / textureSize; - fragColor = roundedRectangle(uv * blurSize, texture(texUnit, tex).rgb); -} diff --git a/src/shaders/upsample.frag b/src/shaders/upsample.frag deleted file mode 100644 index 4cea5bb03..000000000 --- a/src/shaders/upsample.frag +++ /dev/null @@ -1,204 +0,0 @@ -uniform float topCornerRadius; -uniform float bottomCornerRadius; -uniform float antialiasing; - -uniform vec2 blurSize; -uniform float opacity; - -vec4 roundedRectangle(vec2 fragCoord, vec3 texture) -{ - if (topCornerRadius == 0 && bottomCornerRadius == 0) { - return vec4(texture, opacity); - } - - vec2 halfblurSize = blurSize * 0.5; - vec2 p = fragCoord - halfblurSize; - float radius = 0.0; - if ((fragCoord.y <= bottomCornerRadius) - && (fragCoord.x <= bottomCornerRadius || fragCoord.x >= blurSize.x - bottomCornerRadius)) { - radius = bottomCornerRadius; - p.y -= radius; - } else if ((fragCoord.y >= blurSize.y - topCornerRadius) - && (fragCoord.x <= topCornerRadius || fragCoord.x >= blurSize.x - topCornerRadius)) { - radius = topCornerRadius; - p.y += radius; - } - float distance = length(max(abs(p) - (halfblurSize + vec2(0.0, radius)) + radius, 0.0)) - radius; - - float s = smoothstep(0.0, antialiasing, distance); - return vec4(texture, mix(1.0, 0.0, s) * opacity); -} - - -uniform sampler2D texUnit; -uniform float offset; -uniform vec2 halfpixel; - -uniform bool noise; -uniform sampler2D noiseTexture; -uniform vec2 noiseTextureSize; - -uniform float edgeSizePixels; -uniform float refractionCornerRadiusPixels; -uniform float refractionStrength; -uniform float refractionNormalPow; -uniform float refractionRGBFringing; -uniform int refractionTextureRepeatMode; -uniform int refractionMode; // 0: Basic, 1: Concave - -varying vec2 uv; - -vec2 applyTextureRepeatMode(vec2 coord) -{ - if (refractionTextureRepeatMode == 0) { - return clamp(coord, 0.0, 1.0); - } else if (refractionTextureRepeatMode == 1) { - // flip on both axes - vec2 flip = mod(coord, 2.0); - - vec2 result = coord; - if (flip.x > 1.0) { - result.x = 1.0 - mod(coord.x, 1.0); - } else { - result.x = mod(coord.x, 1.0); - } - - if (flip.y > 1.0) { - result.y = 1.0 - mod(coord.y, 1.0); - } else { - result.y = mod(coord.y, 1.0); - } - - return result; - } - return coord; -} - -// Concave lens-style radial mapping around the rect center, shaped by distance to edge -vec2 concaveLensCoord(vec2 uv, float strength, float fringing, float dist, vec2 halfBlurSize) -{ - // Edge proximity: 0 in the deep interior, 1 near the rounded rectangle edge - float edgeProximity = clamp(1.0 + dist / edgeSizePixels, 0.0, 1.0); - float shaped = sin(pow(edgeProximity, refractionNormalPow) * 1.57079632679); - - vec2 fromCenter = uv - vec2(0.5); - - float scaleR = 1.0 - shaped * strength * (1.0 + fringing); - float scaleG = 1.0 - shaped * strength; - float scaleB = 1.0 - shaped * strength * (1.0 - fringing); - - // Return per-channel lens coords packed in vec2 three times via caller - // Caller samples each channel separately with the right scale - // Here we just return the green channel scale as a convenience; R and B will be built in caller - return vec2(0.5) + fromCenter * scaleG; -} - -// source: https://iquilezles.org/articles/distfunctions2d/ -// https://www.shadertoy.com/view/4llXD7 -float roundedRectangleDist(vec2 p, vec2 b, float r) -{ - vec2 q = abs(p) - b + r; - return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r; -} - -void main(void) -{ - vec2 offsets[8] = vec2[]( - vec2(-halfpixel.x * 2.0, 0.0), - vec2(-halfpixel.x, halfpixel.y), - vec2(0.0, halfpixel.y * 2.0), - vec2(halfpixel.x, halfpixel.y), - vec2(halfpixel.x * 2.0, 0.0), - vec2(halfpixel.x, -halfpixel.y), - vec2(0.0, -halfpixel.y * 2.0), - vec2(-halfpixel.x, -halfpixel.y) - ); - float weights[8] = float[](1.0, 2.0, 1.0, 2.0, 1.0, 2.0, 1.0, 2.0); - float weightSum = 12.0; - vec4 sum = vec4(0, 0, 0, 0); - - if (refractionStrength > 0) { - vec2 halfBlurSize = 0.5 * blurSize; - vec2 position = uv * blurSize - halfBlurSize.xy; - float cornerR = min(refractionCornerRadiusPixels, min(halfBlurSize.x, halfBlurSize.y)); - float distConcave = roundedRectangleDist(position, halfBlurSize, cornerR); - float distBulge = roundedRectangleDist(position, halfBlurSize, edgeSizePixels); - - // Different refraction behavior depending on mode - if (refractionMode == 1) { - // Concave: lens-like radial mapping with RGB fringing - float fringing = refractionRGBFringing * 0.3; - float baseStrength = 0.2 * refractionStrength; - - // Edge proximity shaping - float edgeProximity = clamp(1.0 + distConcave / edgeSizePixels, 0.0, 1.0); - float shaped = sin(pow(edgeProximity, refractionNormalPow) * 1.57079632679); - - vec2 fromCenter = uv - vec2(0.5); - float scaleR = 1.0 - shaped * baseStrength * (1.0 + fringing); - float scaleG = 1.0 - shaped * baseStrength; - float scaleB = 1.0 - shaped * baseStrength * (1.0 - fringing); - - vec2 coordR = applyTextureRepeatMode(vec2(0.5) + fromCenter * scaleR); - vec2 coordG = applyTextureRepeatMode(vec2(0.5) + fromCenter * scaleG); - vec2 coordB = applyTextureRepeatMode(vec2(0.5) + fromCenter * scaleB); - - for (int i = 0; i < 8; ++i) { - vec2 off = offsets[i] * offset; - sum.r += texture2D(texUnit, coordR + off).r * weights[i]; - sum.g += texture2D(texUnit, coordG + off).g * weights[i]; - sum.b += texture2D(texUnit, coordB + off).b * weights[i]; - sum.a += texture2D(texUnit, coordG + off).a * weights[i]; - } - - sum /= weightSum; - } else { - // Basic: convex/bulge-like along inward normal from the rounded-rect edge - float concaveFactor = pow(clamp(1.0 + distBulge / edgeSizePixels, 0.0, 1.0), refractionNormalPow); - - // Initial 2D normal - const float h = 1.0; - vec2 gradient = vec2( - roundedRectangleDist(position + vec2(h, 0), halfBlurSize, edgeSizePixels) - roundedRectangleDist(position - vec2(h, 0), halfBlurSize, edgeSizePixels), - roundedRectangleDist(position + vec2(0, h), halfBlurSize, edgeSizePixels) - roundedRectangleDist(position - vec2(0, h), halfBlurSize, edgeSizePixels) - ); - - vec2 normal = length(gradient) > 1e-6 ? -normalize(gradient) : vec2(0.0, 1.0); - - float finalStrength = 0.2 * concaveFactor * refractionStrength; - - // Different refraction offsets for each color channel - float fringingFactor = refractionRGBFringing * 0.3; - vec2 refractOffsetR = normal.xy * (finalStrength * (1.0 + fringingFactor)); // Red bends most - vec2 refractOffsetG = normal.xy * finalStrength; - vec2 refractOffsetB = normal.xy * (finalStrength * (1.0 - fringingFactor)); // Blue bends least - - vec2 coordR = applyTextureRepeatMode(uv - refractOffsetR); - vec2 coordG = applyTextureRepeatMode(uv - refractOffsetG); - vec2 coordB = applyTextureRepeatMode(uv - refractOffsetB); - - for (int i = 0; i < 8; ++i) { - vec2 off = offsets[i] * offset; - sum.r += texture2D(texUnit, coordR + off).r * weights[i]; - sum.g += texture2D(texUnit, coordG + off).g * weights[i]; - sum.b += texture2D(texUnit, coordB + off).b * weights[i]; - sum.a += texture2D(texUnit, coordG + off).a * weights[i]; - } - - sum /= weightSum; - } - } else { - for (int i = 0; i < 8; ++i) { - vec2 off = offsets[i] * offset; - sum += texture2D(texUnit, uv + off) * weights[i]; - } - - sum /= weightSum; - } - - if (noise) { - sum += vec4(texture2D(noiseTexture, vec2(uv.x, 1.0 - uv.y) * blurSize / noiseTextureSize).rrr, 0.0); - } - - gl_FragColor = roundedRectangle(uv * blurSize, sum.rgb); -} \ No newline at end of file diff --git a/src/shaders/upsample_core.frag b/src/shaders/upsample_core.frag deleted file mode 100644 index 80f5ba2ea..000000000 --- a/src/shaders/upsample_core.frag +++ /dev/null @@ -1,198 +0,0 @@ -#version 140 - -uniform float topCornerRadius; -uniform float bottomCornerRadius; -uniform float antialiasing; - -uniform vec2 blurSize; -uniform float opacity; - -vec4 roundedRectangle(vec2 fragCoord, vec3 texture) -{ - if (topCornerRadius == 0 && bottomCornerRadius == 0) { - return vec4(texture, opacity); - } - - vec2 halfblurSize = blurSize * 0.5; - vec2 p = fragCoord - halfblurSize; - float radius = 0.0; - if ((fragCoord.y <= bottomCornerRadius) - && (fragCoord.x <= bottomCornerRadius || fragCoord.x >= blurSize.x - bottomCornerRadius)) { - radius = bottomCornerRadius; - p.y -= radius; - } else if ((fragCoord.y >= blurSize.y - topCornerRadius) - && (fragCoord.x <= topCornerRadius || fragCoord.x >= blurSize.x - topCornerRadius)) { - radius = topCornerRadius; - p.y += radius; - } - float distance = length(max(abs(p) - (halfblurSize + vec2(0.0, radius)) + radius, 0.0)) - radius; - - float s = smoothstep(0.0, antialiasing, distance); - return vec4(texture, mix(1.0, 0.0, s) * opacity); -} - - -uniform sampler2D texUnit; -uniform float offset; -uniform vec2 halfpixel; - -uniform bool noise; -uniform sampler2D noiseTexture; -uniform vec2 noiseTextureSize; - -uniform float edgeSizePixels; -uniform float refractionCornerRadiusPixels; -uniform float refractionStrength; -uniform float refractionNormalPow; -uniform float refractionRGBFringing; -uniform int refractionTextureRepeatMode; -uniform int refractionMode; // 0: Basic, 1: Concave - -in vec2 uv; -out vec4 fragColor; - -vec2 applyTextureRepeatMode(vec2 coord) -{ - if (refractionTextureRepeatMode == 0) { - return clamp(coord, 0.0, 1.0); - } else if (refractionTextureRepeatMode == 1) { - // flip on both axes - vec2 flip = mod(coord, 2.0); - - vec2 result = coord; - if (flip.x > 1.0) { - result.x = 1.0 - mod(coord.x, 1.0); - } else { - result.x = mod(coord.x, 1.0); - } - - if (flip.y > 1.0) { - result.y = 1.0 - mod(coord.y, 1.0); - } else { - result.y = mod(coord.y, 1.0); - } - - return result; - } - return coord; -} - -// Concave lens-style radial mapping around the rect center, shaped by distance to edge -vec2 concaveLensCoord(vec2 uv, float strength, float fringing, float dist, vec2 halfBlurSize) -{ - float edgeProximity = clamp(1.0 + dist / edgeSizePixels, 0.0, 1.0); - float shaped = sin(pow(edgeProximity, refractionNormalPow) * 1.57079632679); - - vec2 fromCenter = uv - vec2(0.5); - - float scaleR = 1.0 - shaped * strength * (1.0 + fringing); - float scaleG = 1.0 - shaped * strength; - float scaleB = 1.0 - shaped * strength * (1.0 - fringing); - - return vec2(0.5) + fromCenter * scaleG; -} - -// source: https://iquilezles.org/articles/distfunctions2d/ -// https://www.shadertoy.com/view/4llXD7 -float roundedRectangleDist(vec2 p, vec2 b, float r) -{ - vec2 q = abs(p) - b + r; - return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r; -} - -void main(void) -{ - vec2 offsets[8] = vec2[]( - vec2(-halfpixel.x * 2.0, 0.0), - vec2(-halfpixel.x, halfpixel.y), - vec2(0.0, halfpixel.y * 2.0), - vec2(halfpixel.x, halfpixel.y), - vec2(halfpixel.x * 2.0, 0.0), - vec2(halfpixel.x, -halfpixel.y), - vec2(0.0, -halfpixel.y * 2.0), - vec2(-halfpixel.x, -halfpixel.y) - ); - float weights[8] = float[](1.0, 2.0, 1.0, 2.0, 1.0, 2.0, 1.0, 2.0); - float weightSum = 12.0; - vec4 sum = vec4(0, 0, 0, 0); - - if (refractionStrength > 0) { - vec2 halfBlurSize = 0.5 * blurSize; - vec2 position = uv * blurSize - halfBlurSize.xy; - float cornerR = min(refractionCornerRadiusPixels, min(halfBlurSize.x, halfBlurSize.y)); - float distConcave = roundedRectangleDist(position, halfBlurSize, cornerR); - float distBulge = roundedRectangleDist(position, halfBlurSize, edgeSizePixels); - if (refractionMode == 1) { - float fringing = refractionRGBFringing * 0.3; - float baseStrength = 0.2 * refractionStrength; - - float edgeProximity = clamp(1.0 + distConcave / edgeSizePixels, 0.0, 1.0); - float shaped = sin(pow(edgeProximity, refractionNormalPow) * 1.57079632679); - - vec2 fromCenter = uv - vec2(0.5); - float scaleR = 1.0 - shaped * baseStrength * (1.0 + fringing); - float scaleG = 1.0 - shaped * baseStrength; - float scaleB = 1.0 - shaped * baseStrength * (1.0 - fringing); - - vec2 coordR = applyTextureRepeatMode(vec2(0.5) + fromCenter * scaleR); - vec2 coordG = applyTextureRepeatMode(vec2(0.5) + fromCenter * scaleG); - vec2 coordB = applyTextureRepeatMode(vec2(0.5) + fromCenter * scaleB); - - for (int i = 0; i < 8; ++i) { - vec2 off = offsets[i] * offset; - sum.r += texture(texUnit, coordR + off).r * weights[i]; - sum.g += texture(texUnit, coordG + off).g * weights[i]; - sum.b += texture(texUnit, coordB + off).b * weights[i]; - sum.a += texture(texUnit, coordG + off).a * weights[i]; - } - - sum /= weightSum; - } else { - float concaveFactor = pow(clamp(1.0 + distBulge / edgeSizePixels, 0.0, 1.0), refractionNormalPow); - - // Initial 2D normal - const float h = 1.0; - vec2 gradient = vec2( - roundedRectangleDist(position + vec2(h, 0), halfBlurSize, edgeSizePixels) - roundedRectangleDist(position - vec2(h, 0), halfBlurSize, edgeSizePixels), - roundedRectangleDist(position + vec2(0, h), halfBlurSize, edgeSizePixels) - roundedRectangleDist(position - vec2(0, h), halfBlurSize, edgeSizePixels) - ); - - vec2 normal = length(gradient) > 1e-6 ? -normalize(gradient) : vec2(0.0, 1.0); - - float finalStrength = 0.2 * concaveFactor * refractionStrength; - - // Different refraction offsets for each color channel - float fringingFactor = refractionRGBFringing * 0.3; - vec2 refractOffsetR = normal.xy * (finalStrength * (1.0 + fringingFactor)); // Red bends most - vec2 refractOffsetG = normal.xy * finalStrength; - vec2 refractOffsetB = normal.xy * (finalStrength * (1.0 - fringingFactor)); // Blue bends least - - vec2 coordR = applyTextureRepeatMode(uv - refractOffsetR); - vec2 coordG = applyTextureRepeatMode(uv - refractOffsetG); - vec2 coordB = applyTextureRepeatMode(uv - refractOffsetB); - - for (int i = 0; i < 8; ++i) { - vec2 off = offsets[i] * offset; - sum.r += texture(texUnit, coordR + off).r * weights[i]; - sum.g += texture(texUnit, coordG + off).g * weights[i]; - sum.b += texture(texUnit, coordB + off).b * weights[i]; - sum.a += texture(texUnit, coordG + off).a * weights[i]; - } - - sum /= weightSum; - } - } else { - for (int i = 0; i < 8; ++i) { - vec2 off = offsets[i] * offset; - sum += texture(texUnit, uv + off) * weights[i]; - } - - sum /= weightSum; - } - - if (noise) { - sum += vec4(texture(noiseTexture, vec2(uv.x, 1.0 - uv.y) * blurSize / noiseTextureSize).rrr, 0.0); - } - - fragColor = roundedRectangle(uv * blurSize, sum.rgb); -} \ No newline at end of file From c881e295f842b0d74c397dd82060bcef9537ff99 Mon Sep 17 00:00:00 2001 From: German Date: Tue, 12 Aug 2025 22:18:48 +0200 Subject: [PATCH 4/4] refraction: also disable labels --- src/kcm/blur_config.cpp | 7 +++++++ src/kcm/blur_config.ui | 8 ++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/kcm/blur_config.cpp b/src/kcm/blur_config.cpp index cabef7893..ca368ef68 100644 --- a/src/kcm/blur_config.cpp +++ b/src/kcm/blur_config.cpp @@ -16,6 +16,7 @@ #include #include #include +#include namespace KWin { @@ -45,6 +46,12 @@ BlurEffectConfig::BlurEffectConfig(QObject *parent, const KPluginMetaData &data) if (ui.labelRefractionCornerRadius) { ui.labelRefractionCornerRadius->setEnabled(concave); } + if (ui.labelRefractionCornerRadiusSquare) { + ui.labelRefractionCornerRadiusSquare->setEnabled(concave); + } + if (ui.labelRefractionCornerRadiusRound) { + ui.labelRefractionCornerRadiusRound->setEnabled(concave); + } }; if (ui.kcfg_RefractionMode) { connect(ui.kcfg_RefractionMode, QOverload::of(&QComboBox::currentIndexChanged), this, [updateEdgeBehaviorEnabled](int){ updateEdgeBehaviorEnabled(); }); diff --git a/src/kcm/blur_config.ui b/src/kcm/blur_config.ui index 07dab0215..b917560e4 100644 --- a/src/kcm/blur_config.ui +++ b/src/kcm/blur_config.ui @@ -628,8 +628,8 @@ - - + + Square @@ -660,8 +660,8 @@ - - + + Round