Improve blur quality with configurable sample count
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
This commit is contained in:
parent
fa503d3f90
commit
bb2ce2552b
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -3325,6 +3325,7 @@ _imageEffector:
|
||||||
scale: "サイズ"
|
scale: "サイズ"
|
||||||
size: "サイズ"
|
size: "サイズ"
|
||||||
radius: "半径"
|
radius: "半径"
|
||||||
|
samples: "サンプル数"
|
||||||
offset: "位置"
|
offset: "位置"
|
||||||
color: "色"
|
color: "色"
|
||||||
opacity: "不透明度"
|
opacity: "不透明度"
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
Loading…
Reference in New Issue