Reputation: 882
I'm using OpenGL ES 3. I want to draw transparent images. Some pixels of the image may have transparency from 0% to 100%. I also want to be able to set an argb value to change the whole image's color and transparency.
I have ended up with the following solution.
Rendering:
GL.Enable(EnableCap.Blend);
GL.BlendFunc(BlendingFactorSrc.One, BlendingFactorDest.OneMinusSrcAlpha);
GL.Uniform4(shader.UniformColor, color);
GL.BindTexture(TextureTarget.Texture2D, sprite.TextureId);
...
Fragment Shader:
void main()
{
fragColor = texture(text, textureCoordinate) * color;
}
The Problem is now that when i set a transparent color, the blending is done wrong, and the image gets brighter. It doesn't seem to be blended with the background correctly. When I use BlendingFactorSrc.SrcAlpha the transparency is right but there is a dark border around the image.
Does anyone have a solution to this problem? I have researched a lot and the solution for the dark border was to use the blending source factor one. But as decribed above i get another problem with that.
Thanks for your help!
Edit: Here's an illustration of the problem: https://i.sstatic.net/LAw3K.png
Upvotes: 0
Views: 1074
Reputation: 882
There were 2 different Problems.
1: Seems like it's necessary to process the image data before loading it into a texture. I came across "premultiplied alpha" which seems to be necessary in my case. You basically multiply each rgb value by the alpha value. I still don't know why it has to be that complicated and you can't set it directly in opengl:
private void PremultiplyAlpha(byte[] data)
{
for (int i = 0; i < data.Length; i += 4)
{
int alpha = data[i + 3]; // A
float factor = alpha > 0 ? 255f / alpha : 1f;
data[i] = (byte)(data[i] * factor); // R
data[i + 1] = (byte)(data[i + 1] * factor); // G
data[i + 2] = (byte)(data[i + 2] * factor); // B
}
}
…
byte[] data = GetPixelArray(bitmap);
PremultiplyAlpha(data);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, width, height, 0, OpenTK.Graphics.ES30.PixelFormat.Rgba, PixelType.UnsignedByte, data);
2: There's still a border when the texture is rendered in a different scale and uses mipmap. I had to change the following setting from NearestMipmapLinear to Nearest:
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMagFilter.Nearest);// (int)All.NearestMipmapLinear);
And I can use the following blend function:
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
Upvotes: 1