Search code examples
javascripttextureswebglblending

webgl 2d blending two transparent textures on top of each other


I am trying to blend two textures with an alpha channel over each other.

enter image description here

After looking through the net it appeared that there are no simple ways to solve this. I tried this trick in the fragment shader:

if(gl_FragColor.a < 0.5){
    discard;
}

This works for simpler textures with not a lot of alpha variations, like the human sprite in the background. But I want to be able to work with more complex images like the gradient sprite which doesn't work at all.

This is my fragment shader:

precision mediump float;
varying vec3 fragColor;
varying highp vec2 vTextureCoord;
uniform sampler2D uSampler;
void main()
{
    vec4 tex = texture2D(uSampler, vTextureCoord);
    gl_FragColor = tex * vec4(fragColor, 1.0);
    if(gl_FragColor.a < 0.5){
    discard;
}
}'

This is my vertex shader:

precision mediump float;

attribute vec3 vertPosition;
attribute vec3 vertColor;
attribute vec2 aTextureCoord;
varying vec3 fragColor;
varying highp vec2 vTextureCoord;
uniform mat4 uPMatrix;
uniform mat4 uMVMatrix;

uniform vec2 uvOffset;
uniform vec2 uvScale;

void main()
{
  fragColor = vertColor;
  gl_Position = uPMatrix * uMVMatrix * vec4(vertPosition.x, vertPosition.y, vertPosition.z, 1.0);
  vTextureCoord = aTextureCoord * uvScale + uvOffset;
}

This is a part of the gl setup I use:

gl.enable(gl.DEPTH_TEST);
gl.enable(gl.BLEND);
gl.blendEquation(gl.FUNC_ADD);
gl.blendFunc(gl.SRC_ALPHA, gl.ON);

Currently all sprites are being drawn on the same z axis, 0. However I don't know if the is the source of the problem as I tested giving each object a random z value and the problem persisted.

Edit: In response to Rabbid76's comment.

This works verry well! The alpha is blended but the only problem is that the sprites look "burned":

enter image description here

I tried to alter the fragment shader to this:

<strike>gl_FragColor = tex * vec4(tex.rgb, tex.a);</strike>

But it still looks burned.

Edit 2

I solved it it. gl_FragColor should b:

gl_FragColor = vec4(tex.rgb, tex.a);

and not

gl_FragColor = vec4(fragColor* tex.rgb, tex.a);

otherwise it creates a burn blending effect


Solution

  • Currently all sprites are being drawn on the same z axis, 0.

    Since the dept test is enabled (gl.enable(gl.DEPTH_TEST)), and the default depth function (depthFunc) is gl.LESS, the second drawn sprite won't pass the depth test.
    You have to disable the depth test:

    gl.disable(gl.DEPTH_TEST);
    


    Further I recommend to adapt the blend function (blendFunc):

    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    

    Or you use Alpha premultiplication. Therefore you have to adapt the fragment shader:

    gl_FragColor = tex * vec4(fragColor * tex.rgb, tex.a);
    

    And you have to use the following blend function (blendFunc):

    gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
    

    Note, you don't need if(gl_FragColor.a < 0.5) discard; any more.