具有 alpha 透明度的高斯模糊句柄

Gaussian blur handle with alpha transparency

 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";

是的,不幸的是,透明度存在整体问题,当像素透明时其他颜色通道会发生什么。如果您尝试在没有 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...(通过对 (1+2+5+2+1) 求和得到 11)所以您以 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...) 这样更容易解释。

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

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

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

其中 fGamma0.01.0 之间的 float(我在示例中使用了 fGamma=5.0)。

这里有一个link很好的解释: https://learnopengl.com/#!Advanced-Lighting/Gamma-Correction