From bb2ce2552bf37a4e92c4639c37baa86cdd4a057f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Sep 2025 02:17:17 +0000 Subject: [PATCH] Improve blur quality with configurable sample count Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> --- locales/en-US.yml | 1 + locales/ja-JP.yml | 1 + .../src/utility/image-effector/fxs/blur.ts | 67 ++++++++++++------- 3 files changed, 46 insertions(+), 23 deletions(-) diff --git a/locales/en-US.yml b/locales/en-US.yml index e54060c267..faa7f5e59e 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -3211,6 +3211,7 @@ _imageEffector: scale: "Size" size: "Size" radius: "Radius" + samples: "Samples" color: "Color" opacity: "Opacity" normalize: "Normalize" diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index dcb1701a06..16d1fe0ac6 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -3325,6 +3325,7 @@ _imageEffector: scale: "サイズ" size: "サイズ" radius: "半径" + samples: "サンプル数" offset: "位置" color: "色" opacity: "不透明度" diff --git a/packages/frontend/src/utility/image-effector/fxs/blur.ts b/packages/frontend/src/utility/image-effector/fxs/blur.ts index 31324e08d3..3a6fd333fe 100644 --- a/packages/frontend/src/utility/image-effector/fxs/blur.ts +++ b/packages/frontend/src/utility/image-effector/fxs/blur.ts @@ -13,35 +13,47 @@ in vec2 in_uv; uniform sampler2D in_texture; uniform vec2 in_resolution; uniform float u_radius; +uniform int u_samples; out vec4 out_color; void main() { vec2 texelSize = 1.0 / in_resolution; vec4 result = vec4(0.0); + float totalWeight = 0.0; - // Simple box blur with fixed kernel size for better performance - // This avoids dynamic loops which can be slow on some GPUs - float radius = min(u_radius, 8.0); // Clamp to reasonable maximum + // High-quality blur with configurable sample count + int samples = min(u_samples, 128); // Clamp to reasonable maximum + int radius = int(u_radius); - // Sample in a cross pattern for efficiency - result += texture(in_texture, in_uv); // Center + // Use a more sophisticated sampling pattern for better quality + for (int x = -radius; x <= radius; x++) { + for (int y = -radius; y <= radius; y++) { + // Calculate distance from center for weighting + float distance = length(vec2(float(x), float(y))); + + // Skip samples beyond the circular radius + if (distance > u_radius) continue; + + // Calculate sample position + vec2 offset = vec2(float(x), float(y)) * texelSize; + vec2 sampleUV = in_uv + offset; + + // Only sample if within texture bounds + if (sampleUV.x >= 0.0 && sampleUV.x <= 1.0 && sampleUV.y >= 0.0 && sampleUV.y <= 1.0) { + // Use Gaussian-like weighting for better quality + float weight = exp(-(distance * distance) / (2.0 * (u_radius * 0.5) * (u_radius * 0.5))); + result += texture(in_texture, sampleUV) * weight; + totalWeight += weight; + + // Limit actual samples processed for performance + samples--; + if (samples <= 0) break; + } + } + if (samples <= 0) break; + } - // Horizontal samples - result += texture(in_texture, in_uv + vec2(-radius * texelSize.x, 0.0)); - result += texture(in_texture, in_uv + vec2(radius * texelSize.x, 0.0)); - - // Vertical samples - result += texture(in_texture, in_uv + vec2(0.0, -radius * texelSize.y)); - result += texture(in_texture, in_uv + vec2(0.0, radius * texelSize.y)); - - // Diagonal samples for better quality - result += texture(in_texture, in_uv + vec2(-radius * texelSize.x, -radius * texelSize.y)); - result += texture(in_texture, in_uv + vec2(radius * texelSize.x, -radius * texelSize.y)); - result += texture(in_texture, in_uv + vec2(-radius * texelSize.x, radius * texelSize.y)); - result += texture(in_texture, in_uv + vec2(radius * texelSize.x, radius * texelSize.y)); - - // Average the samples - out_color = result / 9.0; + out_color = totalWeight > 0.0 ? result / totalWeight : texture(in_texture, in_uv); } `; @@ -49,18 +61,27 @@ export const FX_blur = defineImageEffectorFx({ id: 'blur', name: i18n.ts._imageEffector._fxs.blur, shader, - uniforms: ['radius'] as const, + uniforms: ['radius', 'samples'] as const, params: { radius: { label: i18n.ts._imageEffector._fxProps.radius, type: 'number', default: 3.0, min: 0.0, - max: 10.0, + max: 15.0, step: 0.5, }, + samples: { + label: i18n.ts._imageEffector._fxProps.samples, + type: 'number', + default: 64, + min: 9, + max: 128, + step: 1, + }, }, main: ({ gl, u, params }) => { gl.uniform1f(u.radius, params.radius); + gl.uniform1i(u.samples, params.samples); }, }); \ No newline at end of file