我有一个我试图模糊的对象。
以下是一个示例看起来没有模糊,然后用这种技术模糊:
显然问题是模糊物体周围的白光。
我想我掌握了为什么会这样的基本概念。虽然FBO中对象周围的像素是透明的,但它们仍保持颜色(1,1,1),因此,该颜色会混合到模糊中。
我只是不知道我会采取什么措施来解决这个问题?
这是我的水平模糊着色器,垂直方向大致相同:
hBlur.vert
uniform mat4 u_projTrans;
uniform float u_blurPixels;
uniform float u_texelWidth;
attribute vec4 a_position;
attribute vec2 a_texCoord0;
attribute vec4 a_color;
varying vec2 v_texCoord;
varying vec2 v_blurTexCoords[14];
void main()
{
v_texCoord = a_texCoord0;
gl_Position = u_projTrans * a_position;
float blurDistance6 = u_blurPixels * u_texelWidth;
float blurDistance5 = blurDistance6 * 0.84;
float blurDistance4 = blurDistance6 * 0.70;
float blurDistance3 = blurDistance6 * 0.56;
float blurDistance2 = blurDistance6 * 0.42;
float blurDistance1 = blurDistance6 * 0.28;
float blurDistance0 = blurDistance6 * 0.14;
v_blurTexCoords[ 0] = v_texCoord + vec2(-blurDistance6, 0.0);
v_blurTexCoords[ 1] = v_texCoord + vec2(-blurDistance5, 0.0);
v_blurTexCoords[ 2] = v_texCoord + vec2(-blurDistance4, 0.0);
v_blurTexCoords[ 3] = v_texCoord + vec2(-blurDistance3, 0.0);
v_blurTexCoords[ 4] = v_texCoord + vec2(-blurDistance2, 0.0);
v_blurTexCoords[ 5] = v_texCoord + vec2(-blurDistance1, 0.0);
v_blurTexCoords[ 6] = v_texCoord + vec2(-blurDistance0, 0.0);
v_blurTexCoords[ 7] = v_texCoord + vec2( blurDistance0, 0.0);
v_blurTexCoords[ 8] = v_texCoord + vec2( blurDistance1, 0.0);
v_blurTexCoords[ 9] = v_texCoord + vec2( blurDistance2, 0.0);
v_blurTexCoords[10] = v_texCoord + vec2( blurDistance3, 0.0);
v_blurTexCoords[11] = v_texCoord + vec2( blurDistance4, 0.0);
v_blurTexCoords[12] = v_texCoord + vec2( blurDistance5, 0.0);
v_blurTexCoords[13] = v_texCoord + vec2( blurDistance6, 0.0);
}
blur.frag
uniform sampler2D u_texture;
varying vec2 v_texCoord;
varying vec2 v_blurTexCoords[14];
void main()
{
gl_FragColor = vec4(0.0);
gl_FragColor += texture2D(u_texture, v_blurTexCoords[ 0]) * 0.0044299121055113265;
gl_FragColor += texture2D(u_texture, v_blurTexCoords[ 1]) * 0.00895781211794;
gl_FragColor += texture2D(u_texture, v_blurTexCoords[ 2]) * 0.0215963866053;
gl_FragColor += texture2D(u_texture, v_blurTexCoords[ 3]) * 0.0443683338718;
gl_FragColor += texture2D(u_texture, v_blurTexCoords[ 4]) * 0.0776744219933;
gl_FragColor += texture2D(u_texture, v_blurTexCoords[ 5]) * 0.115876621105;
gl_FragColor += texture2D(u_texture, v_blurTexCoords[ 6]) * 0.147308056121;
gl_FragColor += texture2D(u_texture, v_texCoord ) * 0.159576912161;
gl_FragColor += texture2D(u_texture, v_blurTexCoords[ 7]) * 0.147308056121;
gl_FragColor += texture2D(u_texture, v_blurTexCoords[ 8]) * 0.115876621105;
gl_FragColor += texture2D(u_texture, v_blurTexCoords[ 9]) * 0.0776744219933;
gl_FragColor += texture2D(u_texture, v_blurTexCoords[10]) * 0.0443683338718;
gl_FragColor += texture2D(u_texture, v_blurTexCoords[11]) * 0.0215963866053;
gl_FragColor += texture2D(u_texture, v_blurTexCoords[12]) * 0.00895781211794;
gl_FragColor += texture2D(u_texture, v_blurTexCoords[13]) * 0.0044299121055113265;
}
如果我说我完全确定这段代码在做什么,我会撒谎。但总的来说,它只是从半径u_blurPixels
内采样像素,并使用预先确定的高斯权重来总结gl_FragColor
的结果颜色。
如何修改此项以防止因透明背景而产生白光?
答案 0 :(得分:3)
此模糊程序实际上并不适用于透明图像,因此需要进行一些调整。
要查看你在代码中做了什么,你正在应用周围的像素生效取决于他们的距离,然后你将它们标准化,取其平均值,无论你想要什么叫它......我所说的是那个您的因素0.0044299121055113265
,0.00895781211794
已归一化,因此它们的总和始终为1.更自然地,这些值可能是(例如,仅使用3个像素)scales = [1, 5, 1]
,其中结果为{ {1}}。
因此,如果我们退一步,您的代码可以转换为:
(pix[0]*scales[0] + pix[1]*scales[1] + pix[2]*scales[2])/(scales[0]+scales[1]+scales[2])
现在除非我犯了一些错误,否则这段代码应该与你的代码完全相同。它有点灵活,因为如果你在范围( int offset = 7; // Maximum range taking effect
float offsetScales[offset+1] = { // +1 is for the zero offset
0.159576912161,
0.147308056121,
...
};
float sumOfScales = offsetScales[0];
for(int i=0; i<offset; i++) sumOfScales += offsetScales[i+1]*2.0;
gl_FragColor = texture2D(u_texture, v_texCoord)*offsetScales[0];
for(int i=0; i<offset; i++) {
gl_FragColor += texture2D(u_texture, v_blurTexCoords[6-i]) * offsetScales[i+1];
gl_FragColor += texture2D(u_texture, v_blurTexCoords[7+i]) * offsetScales[i+1];
}
gl_FragColor /= sumOfScales; // sumOfScales in current case is always 1.0
)中添加了另一个像素,你可以简单地添加它并将其值设置为offset = 8
,你的颜色永远不会溢出。在您的情况下,您需要调整所有比例,使其总和等于0.0022
。但是没关系,你的方式更接近最优,所以继续使用它,我正在解释这一点,退后一步,找到问题的解决方案......
好的,现在代码更易于维护,让我们看看当alpha通道需要生效时会发生什么。当像素是透明或半透明时,在计算整体颜色时应该花费较少或没有效果。它应该在结束alpha上生效。这意味着除了这些尺度之外,我们还需要采用alpha尺度。但这样做我们需要调整应用颜色的总和:
1.0
此代码可能仍需要进行一些调整,但我希望这能让您走上正确的轨道。在你完成它的工作后,我建议你再次失去所有的循环并像你最初那样(使用新的逻辑)。如果您发布了最终的代码,也会很好。
随意提出任何问题......
答案 1 :(得分:1)
问题在于OpenGL ES使用后倍增alpha(它在硬件上更便宜),而“正确”执行它需要预乘alpha。
您可以在着色器中为每个模糊的样本执行预乘数学运算:
premult.rgb = source.rgb * source.a;
...但是您要混合的每个纹理样本都会产生运行时成本。通常,在纹理创建/压缩过程中,将输入艺术资产预先离线。
如果您需要用于照明计算等的后乘数据,则可以通过将对象颜色挤压到相邻的“透明”像素中来减少错误。 E.g:
请注意,如果着色器正在发射预乘的alpha fragmetn颜色,则需要修复OpenGL混合方程(对GL_ONE
使用srcAlpha
,而不是alpha值)。