0
\$\begingroup\$

I'm having a some problems with blending in OpenGLES2.0

I'm drawing a texture above a framebuffer (with ARGB channels). When I draw a transparent texture over a solid color, the alpha channel is ignored:

Opaque image on black background

In that image, all sprites are alpha=0, but everything is rendering. For blending, I'm using:

glEnable(GL_BLEND);
glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
        

And for clearing the framebuffer:

glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

The strange thing is, the problem only occurs when I draw over a framebuffer with contents. When I draw over an empty framebuffer rgba(0,0,0,0), it works fine.

Fragment shader:

precision mediump float;

uniform int[8] params;
uniform sampler2D texture;
varying vec2 uv;

void main() {
    vec4 color = texture2D(texture, uv);
    color.a *= 0.0;

    gl_FragColor = color;
}
\$\endgroup\$
0

1 Answer 1

0
\$\begingroup\$

Your blending formula:

glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

...is set up for "pre-multiplied alpha blending". This means you're saying to the blending pipeline, "trust me, I've already multiplied the RGB components by alpha, so you don't have to do that step again".

The trouble is, you're not keeping that promise:

void main() {
    vec4 color = texture2D(texture, uv);
    color.a *= 0.0;

    gl_FragColor = color;
}

This multiplies the alpha of the colour by zero, but does not propagate that multiplication into the RGB components. So even if the texture you're reading was in correct pre-multiplied format, the result of this code is not, unless the RGB was already zero anyway.


Solutions:

If you don't want to use pre-multiplied alpha, you can use "layer" blending instead:

glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

This multiplies the RBG components of your fragment shader's output colour by its Alpha component as part of the blending step, so that zero multiplication propagates through to the colour channels as expected.


Or if you prefer to keep using pre-multiplied alpha blending to get the benefits described in the answer I linked above, you'll need to adjust your shader to ensure any multiplier you apply to the alpha channel also gets applied to the colour channels:

void main() {
    vec4 color = texture2D(texture, uv);

    float alphaMultiplier = 0.0; // Usually you want a non-zero value here.
    color *= alphaMultiplier;

    gl_FragColor = color;
}

Note that alphaMultiplier gets applied to the whole color vector: r, g, b, and a components. This keeps the promise that the RGB channels have been pre-multiplied by the alpha value prior to output.

If your texture is not already in pre-multiplied alpha format, you'd instead do it like this:

void main() {
    vec4 color = texture2D(texture, uv);

    float alphaMultiplier = 0.0; // Usually you want a non-zero value here.
    color.a *= alphaMultiplier;

    // Apply pre-multiplication.
    color.rgb *= color.a;

    gl_FragColor = color;
}
\$\endgroup\$
4
  • \$\begingroup\$ What is better solucion for dark borders? \$\endgroup\$ Apr 10 at 18:13
  • \$\begingroup\$ What is the problem to which you are seeking a solution? \$\endgroup\$
    – DMGregory
    Apr 10 at 18:18
  • \$\begingroup\$ when i apply SRC_ALPHA, borders dont look nice. See with GL_ONE: imgur.com/skoVfrz.png See with GL_SRC_ALPHA: i.imgur.com/3IucOKB.png \$\endgroup\$ Apr 10 at 18:26
  • \$\begingroup\$ That's the issue discussed in the answer I linked. To fix that, use pre-multiplied alpha, and ensure that when you multiply alpha by some number, you multiply the RGB by that same number (i.e. keep your promise to the blending pipeline that your output colour has the pre-multiplication applied). \$\endgroup\$
    – DMGregory
    Apr 10 at 18:33

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .