如何在 glsl 着色器中模糊透明纹理?

How to blur a transparent texture in a glsl shader?

我的设置:

乒乓 RGBA FBO,以及两个用于模糊的着色器:一个水平着色器和一个垂直着色器。假设我想模糊一个红色矩形。

混合函数如下:

_gl.blendEquationSeparate(_gl.FUNC_ADD, _gl.FUNC_ADD);
_gl.blendFuncSeparate(_gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA, _gl.ONE, _gl.ONE_MINUS_SRC_ALPHA);

在我的模糊着色器中,我添加了几个这样的片段:

vec4 color = texture2D(u_image, v_tex_coord + vec2(x, y) * u_amount) * weight;

问题:

模糊在不透明纹理上效果很好,但随着它混合越来越多的透明度,颜色变成黑色,就好像所有东西都与黑色背景混合一样。我的清除颜色是0,0,0,0,所以这是有道理的。

问题:

如何获得真正变成透明红色的模糊效果,而不是当 alpha 变为零时红色与越来越多的黑色混合?

我基本上想要与在 Photoshop 中在完全透明的背景上模糊某些内容时的效果相同。我需要预乘 FBO 还是需要以不同方式处理着色器中的片段混合?

要对透明纹理应用模糊,您需要使用 alpha 进行校正。一个像素的简化公式:

resultColor = (sum of pixels of input image) * 1 / K;

通常K是内核大小。

如果您模糊透明纹理,您需要累积 alpha 并将其用作 K。

resultAlpha = (sum of alpha pixels of input image);
resultColor = (sum of pixels of input image) * 1 / resultAlpha;
resultAlpha = resultAlpha * 1 / K;

对于此公式,如果模糊 4 个像素,其中一个不透明,另一个透明,则结果像素的颜色将相同,但 alpha 将小 4 倍。

在我的示例中,所有通道的值都从 0 到 1。

我通过到处使用预乘 alpha 解决了这个问题。

习惯了 Photoshop 的工作方式,我花了一段时间才理解这个概念。感觉有点违反直觉,但是this explanation from Tom Forsyth对我帮助很大。

我所有的着色器现在都将 RGB 值乘以 A:

gl_FragColor = clr.rgb * alpha;

我正在使用不同的混合模式来实现此目的:

_gl.blendEquation(_gl.FUNC_ADD); _gl.blendFunc(_gl.ONE, _gl.ONE_MINUS_SRC_ALPHA);

我必须确保我的 .PNG 图像纹理也被预乘。我这样做是使用:

_gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);