英文:
JS, WebGL: how to apply a blur filter on a video frame multiple times
问题
我想在视频帧上使用WebGL着色器应用模糊滤镜,并将结果呈现在HTML画布上。模糊滤镜应该被多次应用。第一次应用的结果应该成为下一次的源。
我不太熟悉WebGL,但我创建了一个只应用一次模糊滤镜的简单示例。我尝试多次运行“blur”函数并使用“mix”来合并结果,但它不起作用。
演示在这里。 它有两个滑块,用于应用模糊滤镜。第二个应该模糊前一次应用的结果。
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);
}```
<details>
<summary>英文:</summary>
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.](https://jsfiddle.net/liubomyrB/L3sqdvp1/4/) 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*(xx)/(sigmasigma));
}
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);
}
</details>
# 答案1
**得分**: 1
要对图像进行第二次模糊处理,需要通过额外的渲染通道进行操作,例如使用额外的纹理和帧缓冲。
我已经在你的示例中进行了两次渲染尝试 -
```javascript
if (lastWidth != metadata.width) {
prepareOffscreenFrameBuffer(fb, texfb, metadata.width, metadata.height);
lastWidth = metadata.width;
}
// 第一次渲染通道
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);
// 第二次渲染通道
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);
英文:
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);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论