Reputation: 1549
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.
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.
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
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
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
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
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