Ben5
Ben5

Reputation: 882

How to use blending with transparent textures and transparent color?

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

Answers (1)

Ben5
Ben5

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

Related Questions