【问题标题】:Gaussian blur handle with alpha transparency具有 Alpha 透明度的高斯模糊句柄
【发布时间】:2016-02-18 08:19:09
【问题描述】:
  • 说明

    1. 我在android中使用OpenGL es 2.0和glsl shader来做高斯模糊效果。
    2. 如果没有 alpha 通道,它可以正常工作,但是当我有 png 的 alpha 通道时,结果会变得太暗,它会是相同的链接:http://www.jhlabs.com/ip/premultiplied_blur.jpg(中间一个效果有点暗)。
    3. 所有文章都说使用预乘 alpha,但我不知道如何使用它,是否可以帮助我,谢谢。
  • 我如何做模糊

    1. 加载 png 纹理
    2. 创建 FBO 以渲染水平模糊
    3. 创建 FBO 以渲染垂直模糊
    4. 将结果纹理渲染到屏幕
  • 我的着色器文件

	static const char* ULBlurShader_vertexShader =
		"uniform mat4 fpsWorldViewProjectionMatrix;\n"
		"attribute vec4 gInPosition;\n"
		"attribute vec2 gInTex0;\n"
		"varying vec2 varTex0;\n"
		"void main() {\n"
		"	gl_Position = fpsWorldViewProjectionMatrix * gInPosition;\n"
		"	varTex0 = gInTex0.xy;\n"
		"}\n";

	static const char* ULBlurShaderHor_pixelShader =
		"uniform sampler2D sampler0; \n"
		"uniform vec4 fpsGaussBlur; \n"
		"uniform vec4 fpsGaussWeights[65]; \n"
		"varying vec2 varTex0;\n"
		"vec4 gaussBlurHorizontal(const int anRadius,vec2 avBaseCoo, vec2 avSamplerRes)"
		"{ \n"
		"	float fStartX = avBaseCoo.x - anRadius*avSamplerRes.x; \n"
		"	vec4 color = vec4(0, 0, 0, 0); \n"
		"	for (int x = 0; x < anRadius * 2; x++) \n"
		"	{ \n"
		"		color += texture2D(sampler0, vec2(fStartX + x*avSamplerRes.x, avBaseCoo.y))*fpsGaussWeights[x / 4][x % 4]; \n"
		"	} \n"
		"	return color; \n"
		"}\n"
		"void main() {\n"
		"	gl_FragColor.rgba = gaussBlurHorizontal(int(fpsGaussBlur.y), varTex0, vec2(fpsGaussBlur.z, fpsGaussBlur.w)); \n"
		"}\n";

	static const char* ULBlurShaderVert_pixelShader =
		"uniform sampler2D sampler0; \n"
		"uniform vec4 fpsGaussBlur; \n"
		"uniform vec4 fpsGaussWeights[65]; \n"
		"varying vec2 varTex0;\n"
		"vec4 gaussBlurVertical(const int anRadius,vec2 avBaseCoo, vec2 avSamplerRes)"
		"{ \n"
		"	float fStartY = avBaseCoo.y - (anRadius*avSamplerRes.y); \n"
		"	vec4 color = vec4(0, 0, 0, 0); \n"
		"	for(int y=0; y<anRadius*2; y++) \n"
		"	{ \n"
		"		color += texture2D(sampler0, vec2(avBaseCoo.x, fStartY+y*avSamplerRes.y))*fpsGaussWeights[y/4][y%4]; \n"
		"	} \n"
		"	return color; \n"
		"}\n"
		"void main() {\n"
		"	gl_FragColor.rgba = gaussBlurVertical(int(fpsGaussBlur.y), varTex0, vec2(fpsGaussBlur.z, fpsGaussBlur.w));\n"
		"}\n";

【问题讨论】:

    标签: opengl-es glsl shader blur


    【解决方案1】:

    我遇到了同样的问题,尤其是在模糊被 alpha=0.0 像素包围的文本时。文字会逐渐消失,以至于无法看到(中图)。 但是,在模糊之后添加“伽玛校正”步骤后,我设法获得了更好的结果。

    因此,对于您的情况,在完成模糊后,根据以下内容调整 RGBA 值:

    color = pow(color, vec4(1.0/fGamma))
    

    其中fGamma 是介于0.01.0 之间的float(我在示例中使用了fGamma=5.0)。

    这是一个很好的解释的链接: https://learnopengl.com/#!Advanced-Lighting/Gamma-Correction

    【讨论】:

    • 您的意思是“1/fGamma 是 0.0 和 1.0 之间的浮点数”吗?您使用了哪种颜色代码?我尝试了文本模糊的伽玛校正,但文本仍然不可见
    【解决方案2】:

    是的,很遗憾,透明度存在整体问题,当像素透明时,其他颜色通道会发生什么情况。如果您尝试在没有 alpha 透明度的情况下绘制图像(使用 RGB1),您将看到透明像素是黑色的。至少在某些情况下,这似乎很自然,但并非对所有情况都如此。想象一下,您有一个图像,它具有从完全不透明度下降到包括完全透明的纯白色渐变。然后第一个像素将是(1,1,1,1),某个像素将是(1,1,1,0.5),接近尾声可能是(1,1,1,0.00X),然后是下一个alpha 为 0 的像素将变为 (0,0,0,0) 而不是 (1,1,1,0)。所以这是你混合的黑色,你会看到黑色的边框。

    所以看起来 png 导出器做错了,因为所有透明像素仍然应该是红色的,但事实并非如此。如果图像具有不同的颜色(在您的情况下不仅仅是白色或红色),那么导出器可能无法预测这些像素是什么颜色。所以最后这是一个正确的状态。

    因此,在这种情况下,您需要重新平衡透明像素的颜色混合。这也意味着改变你的模糊着色器的工作方式。在计算颜色时需要忽略透明像素(但不是 alpha),或者更好的是,您需要根据透明度、alpha 通道来缩放颜色的效果。

    我会给你一个例子,当以类似于模糊的特定权重对 5 个像素求和时。假设相对比例为 1、2、5、2、1。您现在正在做的是 color = (c1*1 + c2*2 + c3*5 + c4*2 + c5*1)/(1+2+5+2+1),但您在这个等式中有一个优化,您已经有一个标准化的值,您称之为 fpsGaussWeights,在我的示例中是1/11, 2/11, 5/11...(11 是通过对(1+2+5+2+1) 求和得到的)所以你以c1*1/11 + c2*2/11...结尾。现在让我们更改等式以包含 alpha 值。现在原来是

    color = (c1*1*c1.a + c2*2*c2.a + c3*5*c3.a + c4*2*c4.a + c5*1*c5.a)/(1*c1.a+2*c2.a+5*c3.a+2*c4.a+1*c5.a)
    

    但是你只需要在颜色部分这样做

    color.rgb = (c1.rgb*1*c1.a + c2.rgb*2*c2.a + c3.rgb*5*c3.a + c4.rgb*2*c4.a + c5.rgb*1*c5.a)/(1*c1.a+2*c2.a+5*c3.a+2*c4.a+1*c5.a)
    

    而 Alpha 通道保留了之前的模糊方程。如您所见,您可能不再包含权重的归一化值,因为当您的权重值乘以 alpha 通道时,其总和不会是 1.0。

    现在我希望你可以自己在for 循环中优化这个方程。如果您使用低精度,还要注意溢出。

    看看这个作为参考(甚至可能只是工作,但我没有测试它,甚至没有仔细检查它):

        int sampleCount = 5;
        float weights[sampleCount];
    
        vec4 color = vec4(.0);
        float fullWeight = .0;
        for(int i=0; i<sampleCount; i++) {
            float actualWeight = weights[i] * textureColor.a;
            vec4 textureColor = ; // fetch the pixel
            color.rgb += textureColor.rgb * actualWeight; // can produce overflow
            /*
                We may do the computation on the fly so we do not need to divide in the end. This will remove the overflow issue
            color.rgb = (color.rgb*fullWeight + textureColor.rgb*actualWeight)/(fullWeight + actualWeight);
                Leads to:
            float newWeightResult = (fullWeight + actualWeight);
            float leftWeight = fullWeight/(fullWeight + actualWeight); //will be less or equal then 1
            float rightWeight = actualWeight/(fullWeight + actualWeight); //will be less or equal then 1
            color.rgb = color.rgb*leftWeight + textureColor.rgb*rightWeight; // no overflow
                The color.rgb /= fullWeight line needs to be removed when using this
             */
    
            color.a += textureColor.a * weights[i];
            fullWeight += actualWeight;
        }
        color.rgb /= fullWeight;
    

    您使用的重量仍然可以保持原样。我只使用了 (1,2,5...),所以更容易解释。

    【讨论】:

      猜你喜欢
      • 2012-05-02
      • 1970-01-01
      • 1970-01-01
      • 2019-09-11
      • 2019-04-23
      • 2010-09-30
      • 2019-05-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多