3
votes

my fragment shader does two things :

  1. displays original pixel color from texture, v_Color variable is set to r=0,g=0,b=0,a=0
  2. or discards original pixel color to color stored in v_Color. This value is taken if I set alpha in v_Color to 1.0, in other cases is used original pixel's color.

However I have problem in second case if I apply this shader on images which have pixels with alpha == 0.0. I expected that these pixels will be invisible, but they are colored too. In first case I don't have this problem. I need discard pixels with alpha == 0

For example : I have texture of person silhouette which should be colorized, but transparent pixels should be skipped.

At beginning of program I set :

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

and here is my fragment shader :

void main()
{
    vec4 c = vec4(v_Color);
    vec4 p = texture2D( s_texture, v_texCoord);

    gl_FragColor = mix(p, c, c.a);
}

However if I add discard then all works like is expected. Pixels with alpha==0.0 are removed. But I am working with OpenGL ES 2.0, shader will run across different android devices and I read about possible performance issues caused by "if" and "discard". That's why I used "mix" instead "if".

void main()
{
    vec4 c = vec4(v_Color);
    vec4 p = texture2D( s_texture, v_texCoord);

    if (p.a == 0.0) {
        discard;
    }

    gl_FragColor = mix(p, c, c.a);
}

I tried another attempt to fix that. I remember original alpha from texture and I apply it at the end. But it still gives me weird results. gl_FragColor.a = backup_alpha does something with resulted alpha, it is somehow transparent but not fully.

void main()
{
    vec4 c = vec4(v_Color);
    vec4 p = texture2D( s_texture, v_texCoord);
    float backup_alpha = p.a;

    gl_FragColor = mix(p, c, c.a);
    gl_FragColor.a = backup_alpha;
}

enter image description here

Thanks you @Rabbid76 for help, this is correct :

gl_FragColor = vec4(mix(p.rgb, c.rgb*p.a, c.a), p.a);
1

1 Answers

2
votes

When you do

gl_FragColor = mix(p, c, c.a);

then the alpha channel of the texture is discarded, if the c.a == 1.0, because the alpha channel is also read from c.

You've to mix the .rgb components of the texture and the color, but you've to use the alpha channel of the texture (in any case) to solve the issue:

gl_FragColor = vec4(mix(p.rgb, c.rgb, c.a), p.a);

If the color should be black, a that parts, where the alpha channel of the texture is 0.0, then the color has to be multiplied by the alpha channel of the texture, before it is mixed:

gl_FragColor = vec4(mix(p.rgb, c.rgb*p.a, c.a), p.a);

But note, because of texture filtering, it may occur, that some colors which are red from the texture are nit completely transparent. If you want that the output color is either completely opaque ore completely transparent, then you've to compare the alpha channel to a threshold. This can be done by the glsl function step, which returns either 1.0 or 0.0:

gl_FragColor = vec4(mix(p.rgb, c.rgb, c.a), step(0.5, p.a));