I want to apply a blur filter on a video frame using WebGL shader and render result on HTML canvas. The blur filter should be applied multiple times. The result of the first application should be the source for the next and so on.
I'm not familiar with WebGL well, but I created a simple example that applies a blur filter only one time. I tried to run the "blur" function multiple times and used "mix" to merge results, but it doesn't work. Demo is here. It has two sliders that apply a blur filter. The second one should blur the result of the previous application.
precision mediump float;
uniform sampler2D texture0;
uniform sampler2D texture1;
uniform float texWidth;
uniform float texHeight;
uniform float u_sigma;
uniform float u_sigma2;
float CalcGauss( float x, float sigma )
{
return exp(-0.5*(x*x)/(sigma*sigma));
}
vec4 blur(vec2 texCoord, sampler2D texture, float sigma) {
vec2 texC = texCoord;
vec4 texCol = texture2D( texture, texC );
vec4 gaussCol = vec4( texCol.rgb, 1.0 );
vec2 steph = vec2(1.0, 0.0) / texWidth;
for ( int i = 1; i <= 32; ++ i )
{
float weight = CalcGauss( float(i) / 32.0, sigma * 0.5 );
if ( weight < 1.0/255.0 )
break;
texCol = texture2D( texture, texC + steph * float(i) );
gaussCol += vec4( texCol.rgb * weight, weight );
texCol = texture2D( texture, texC - steph * float(i) );
gaussCol += vec4( texCol.rgb * weight, weight );
}
gaussCol.rgb = clamp( gaussCol.rgb / gaussCol.w, 0.0, 1.0 );
return vec4( gaussCol.rgb, 1.0 );
}
void main(void) {
vec2 texCoord = vec2(gl_FragCoord.x/texWidth, 1.0 - (gl_FragCoord.y/texHeight));
vec4 blur0 = blur(texCoord, texture0, u_sigma);
vec4 blur1 = blur(texCoord, texture1, u_sigma2);
gl_FragColor = mix(blur0, blur1, blur0.a);
}
for applying blur for the second time, you have to run it withing additional pass, by using, for example, additional texture and framebuffer.
I've played around with your fiddle to run with 2 passes -
if (lastWidth != metadata.width) {
prepareOffscreenFrameBuffer(fb, texfb, metadata.width, metadata.height);
lastWidth = metadata.width;
}
// first pass
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.viewport(0, 0, metadata.width, metadata.height);
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, videoEl);
gl.uniform1i(texLoc, 0);
gl.uniform1f(texWidthLoc, metadata.width);
gl.uniform1f(texHeightLoc, metadata.height);
gl.uniform1f(sigmaLoc, parseFloat(sigmaInput.value));
gl.uniform1f(sigmaLoc2, parseFloat(sigmaInput2.value));
gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
// second pass
gl.viewport(0, 0, metadata.width, metadata.height);
gl.bindTexture(gl.TEXTURE_2D, texfb);
gl.uniform1i(texLoc, 0);
gl.uniform1f(texWidthLoc, metadata.width);
gl.uniform1f(texHeightLoc, metadata.height);
gl.uniform1f(sigmaLoc, parseFloat(sigmaInput2.value));
gl.uniform1f(sigmaLoc2, parseFloat(sigmaInput.value));
gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
videoEl.requestVideoFrameCallback(processFrame);