Improve blur quality with configurable sample count

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-09-20 02:17:17 +00:00
parent fa503d3f90
commit bb2ce2552b
3 changed files with 46 additions and 23 deletions

View File

@ -3211,6 +3211,7 @@ _imageEffector:
scale: "Size" scale: "Size"
size: "Size" size: "Size"
radius: "Radius" radius: "Radius"
samples: "Samples"
color: "Color" color: "Color"
opacity: "Opacity" opacity: "Opacity"
normalize: "Normalize" normalize: "Normalize"

View File

@ -3325,6 +3325,7 @@ _imageEffector:
scale: "サイズ" scale: "サイズ"
size: "サイズ" size: "サイズ"
radius: "半径" radius: "半径"
samples: "サンプル数"
offset: "位置" offset: "位置"
color: "色" color: "色"
opacity: "不透明度" opacity: "不透明度"

View File

@ -13,35 +13,47 @@ in vec2 in_uv;
uniform sampler2D in_texture; uniform sampler2D in_texture;
uniform vec2 in_resolution; uniform vec2 in_resolution;
uniform float u_radius; uniform float u_radius;
uniform int u_samples;
out vec4 out_color; out vec4 out_color;
void main() { void main() {
vec2 texelSize = 1.0 / in_resolution; vec2 texelSize = 1.0 / in_resolution;
vec4 result = vec4(0.0); vec4 result = vec4(0.0);
float totalWeight = 0.0;
// Simple box blur with fixed kernel size for better performance // High-quality blur with configurable sample count
// This avoids dynamic loops which can be slow on some GPUs int samples = min(u_samples, 128); // Clamp to reasonable maximum
float radius = min(u_radius, 8.0); // Clamp to reasonable maximum int radius = int(u_radius);
// Sample in a cross pattern for efficiency // Use a more sophisticated sampling pattern for better quality
result += texture(in_texture, in_uv); // Center 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)));
// Horizontal samples // Skip samples beyond the circular radius
result += texture(in_texture, in_uv + vec2(-radius * texelSize.x, 0.0)); if (distance > u_radius) continue;
result += texture(in_texture, in_uv + vec2(radius * texelSize.x, 0.0));
// Vertical samples // Calculate sample position
result += texture(in_texture, in_uv + vec2(0.0, -radius * texelSize.y)); vec2 offset = vec2(float(x), float(y)) * texelSize;
result += texture(in_texture, in_uv + vec2(0.0, radius * texelSize.y)); vec2 sampleUV = in_uv + offset;
// Diagonal samples for better quality // Only sample if within texture bounds
result += texture(in_texture, in_uv + vec2(-radius * texelSize.x, -radius * texelSize.y)); if (sampleUV.x >= 0.0 && sampleUV.x <= 1.0 && sampleUV.y >= 0.0 && sampleUV.y <= 1.0) {
result += texture(in_texture, in_uv + vec2(radius * texelSize.x, -radius * texelSize.y)); // Use Gaussian-like weighting for better quality
result += texture(in_texture, in_uv + vec2(-radius * texelSize.x, radius * texelSize.y)); float weight = exp(-(distance * distance) / (2.0 * (u_radius * 0.5) * (u_radius * 0.5)));
result += texture(in_texture, in_uv + vec2(radius * texelSize.x, radius * texelSize.y)); result += texture(in_texture, sampleUV) * weight;
totalWeight += weight;
// Average the samples // Limit actual samples processed for performance
out_color = result / 9.0; samples--;
if (samples <= 0) break;
}
}
if (samples <= 0) break;
}
out_color = totalWeight > 0.0 ? result / totalWeight : texture(in_texture, in_uv);
} }
`; `;
@ -49,18 +61,27 @@ export const FX_blur = defineImageEffectorFx({
id: 'blur', id: 'blur',
name: i18n.ts._imageEffector._fxs.blur, name: i18n.ts._imageEffector._fxs.blur,
shader, shader,
uniforms: ['radius'] as const, uniforms: ['radius', 'samples'] as const,
params: { params: {
radius: { radius: {
label: i18n.ts._imageEffector._fxProps.radius, label: i18n.ts._imageEffector._fxProps.radius,
type: 'number', type: 'number',
default: 3.0, default: 3.0,
min: 0.0, min: 0.0,
max: 10.0, max: 15.0,
step: 0.5, step: 0.5,
}, },
samples: {
label: i18n.ts._imageEffector._fxProps.samples,
type: 'number',
default: 64,
min: 9,
max: 128,
step: 1,
},
}, },
main: ({ gl, u, params }) => { main: ({ gl, u, params }) => {
gl.uniform1f(u.radius, params.radius); gl.uniform1f(u.radius, params.radius);
gl.uniform1i(u.samples, params.samples);
}, },
}); });