Alchitry
Alchitry

Reputation: 1549

OpenGL transparent images have black in them

I am working on a game for Android and I was wondering why whenever I draw images with transparency there seems to always be some black added to the transparent parts. This happens all over and makes some of my effects look strange.

Here is an example. The two circles are only white images with a blur but you can see when one overlaps the other it has a shadow. If I overlap two of the circles say in Inkscape I get pure white where they overlap.

enter image description here

I am using

GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);

for my blending.

Any idea why this happens and how I can avoid it?

Edit: the only thing I can think of is that the two images have the same z so maybe they are blending only with the background instead of each other?

Edit: I changed

GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);

to

GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_DST_ALPHA);

Here is the result I was looking for.

enter image description here

The only thing now is that the transparent images that I had that have a transparent black in them are ignored, which makes sense because I think the destination alpha is 1. Why would One minus source add that gray?

Upvotes: 5

Views: 8900

Answers (4)

PeterSvP
PeterSvP

Reputation: 2056

You can also divide by alpha, because, the problem is, when you export your textures, Image processing software may pre-multiply your Alpha channel. I ended up with alpha division into my fragment shader. The following code is HLSL, but you can easily convert it to GLSL:

float4 main(float4 tc: TEXCOORD0): COLOR0
{
    float4 ts = tex2D(Tex0,tc);

    //Divide out this pre-multiplied alpha
    float3 outColor = ts.rgb / ts.a;

    return float4(outColor, ts.a);
}

Note that this operation is lossy, very lossy and even if it will most likely suffice in cases such as these, it's not a general solution. In your case you can totally ignore the COLOR and serve original alpha AND white in your fragment shader, e.g. return float4(1,1,1,ts.a); (convert to GLSL)

Upvotes: 0

Alchitry
Alchitry

Reputation: 1549

I figured out how to fix it.

I changed

GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);

to

GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);

It turns out that Android pre-multiplies the alpha of textures of PNG images when you load it (making white turn gray).

I added

vColor.r = vColor.r * vColor.a;
vColor.g = vColor.g * vColor.a;
vColor.b = vColor.b * vColor.a;

to my vertex shader to do the multiplying for my other colors.

Upvotes: 12

Tim
Tim

Reputation: 35943

Are you using linear sampling? My thought could be if you have a very small image (less than say 30-40 pixels in dimension), you could be getting interpolated alpha values between the inside of the circle (alpha = 1) to the outside of the circle (alpha = 0). This would give you intermediate alpha values that result in the kind of blur effect.

Try the following code when you have the circle texture bound:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

Maybe you can host the actual texture that you're using such that we can inspect it? Also please post your drawing code if this doesn't help.

Upvotes: 0

Antti
Antti

Reputation: 12489

Are you sure your content is correct? If you don't want the circles to produce any black color the color values in the image should be completely white, and the alpha channel should define the shape of the circle. Now it looks like the image has a white circle with both alpha channel and the color value fading to zero, which leads to a halo.

Upvotes: 1

Related Questions