I have written my own shader that implements Gaussian blur on the frame. and everything works well, including the edge treatment. But for some reason, the blur looks rough and as if streaks/artifacts are visible. I’ll give you a screenshot of how it looks and how it should look. Just in case, I will give you additional information that the rendering is on canvas 530×70. the blur in u_radius is 40. the blur I’m trying to achieve is 40.
That’s what I got
render
What am I trying to achieve
filter
here is my GLSL vertex and fragment shader code
<canvas id="control1" width="530" height="70" style="transform: rotateX(180deg); filter: saturate(1);" class="control"></canvas>
var canvas = document.getElementById('control1');
var canvas1 = document.createElement('canvas')
canvas1.width = 530
canvas1.height = 70
var ctx = canvas1.getContext('2d', { willReadFrequently: true });
var gl = canvas.getContext('webgl');
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, `
attribute vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
`);
gl.compileShader(vertexShader);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, `
precision mediump float;
uniform sampler2D u_image;
uniform vec2 u_resolution;
uniform float u_radius;
float gaussian(float x, float sigma) {
return exp(-0.5 * x * x / (2.0 * sigma * sigma)) / (sqrt(2.0 * 3.14159) * sigma);
}
vec2 mirror(vec2 st) {
vec2 mirrored = mod(st, 2.0);
return mix(mirrored, 2.0 - mirrored, step(1.0, mirrored));
}
void main() {
vec2 st = gl_FragCoord.xy / u_resolution;
vec4 color = vec4(0.0);
float totalWeight = 0.0;
for (int i = -9; i <= 9; i++) {
for (int j = -9; j <= 9; j++) {
vec2 offset = vec2(float(i), float(j)) / u_resolution;
float distance = length(offset);
float weight = gaussian(distance, u_radius);
color += texture2D(u_image, mirror(st + offset)) * weight;
totalWeight += weight;
}
}
gl_FragColor = color / totalWeight;
}
`);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
console.log('Error: ' + gl.getShaderInfoLog(fragmentShader));
}
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
const positionLocation = gl.getAttribLocation(program, 'position');
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1.0, -1.0,
1.0, -1.0,
-1.0, 1.0,
-1.0, 1.0,
1.0, -1.0,
1.0, 1.0
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
const resolutionLocation = gl.getUniformLocation(program, 'u_resolution');
const radiusLocation = gl.getUniformLocation(program, 'u_radius');
gl.uniform2f(resolutionLocation, canvas.width, canvas.height);
gl.uniform1f(radiusLocation, 40);
gl.drawArrays(gl.TRIANGLES, 0, 6);
video.onplaying = function() {
var $this = this;
(function loop() {
if (!video.paused && !video.ended) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillRect(0, 0, canvas.width, canvas.height);
var intersection = getIntersection($this, canvas);
var ratio = calculateAspectRatioFit($this.videoWidth, $this.videoHeight, $this.getBoundingClientRect().width, $this.getBoundingClientRect().height)
ctx.save();
ctx.rect(intersection.x, intersection.y, ratio.width, ratio.height);
ctx.clip();
ctx.globalAlpha = 1;
ctx.drawImage($this, intersection.x, intersection.y, ratio.width, ratio.height);
ctx.restore();
ctx.globalAlpha = 1.0;
var dataTexture = gl.createTexture();
var imageData = ctx.getImageData(0, 0, canvas1.width, canvas1.height);
const imageLocation = gl.getUniformLocation(program, 'u_image');
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, dataTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, imageData);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.uniform1i(imageLocation, 0);
gl.drawArrays(gl.TRIANGLES, 0, 6);
setTimeout(loop, 1000 / 30);
}
})();
}
I tried to change the radius (u_radius) and samples, and it seems that increasing the samples to 100 and a radius of 0.1 I achieved an approximate effect. but it’s still not what I need and it looks bad. It’s as if the blur is applied only on the x-axis.