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"
size: "Size"
radius: "Radius"
samples: "Samples"
color: "Color"
opacity: "Opacity"
normalize: "Normalize"

View File

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

View File

@ -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);
},
});