WebGl Transparency and Alpha Blending problem

A few days ago I ran into a problem with fonts transprency and alpha blending in WebGL.  To see what I’m talking about let me show you a picture with my fonts problem:

wrong Font WebGL

(Click on the picture to zoom in)

You can see that, letters have white pixels in margins, which ruins the font :(. My fonts are exported from FreeType and I generate anti-aliased images for webgl textures . This mean that alpha channel will have different values between 0 and 255.

Now in Webgl I have the common alpha blend code:

............
//loading texture
webglTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.webglTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.ALPHA,gl.ALPHA,gl.UNSIGNED_BYTE, getImage);//getImage is my 256x256 texture; loaded in a HTML image
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.bindTexture(gl.TEXTURE_2D, null);
.............
//draw
gl.clearColor(0.466, 0.466, 0.466, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.disable(gl.DEPTH_TEST);
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
//load buffers etc
.................
//fragment shader
fragmentShader = 'precision mediump float;\n' +
' varying vec2 vTexCoord; \n' +
' uniform sampler2D texture1; \n' +
' uniform vec4 uTextColor; \n' +
' void main() { \n' +
'gl_FragColor = vec4(uTextColor.rgb, texture2D(texture1,vTexCoord).a); \n' +
'} \n';

First, I tried to write my own blendfunc in shader. This worked fine, but I had to give background color as an uniform. This is not a good solution because you can have several colors under the text.

So I began to analyse and investigate why my font is blending with something white. What could it be?… Oh let’s change the html canvas background-color to black:

<canvas id="webgl" style="width:100%; height:100%;position: absolute;overflow: hidden; background-color: black;"></canvas>.  
good Font WebGL

The result it’s ok (for the eye) but why WebGl works like this?

I discovered that webgl is composited with  the html page and use pre-multiplied alpha by default. This means that for a RGB color like 1,0,1 with alpha 0.5 you will get something like 0.5 ,0 , 0.5. This might help you or not, depends on what result you want.

To get rid of this you can modify the fragment shader again.


fragmentShader = '#ifdef GL_ES \n' +
' precision highp float;\n' +
' #endif \n' +
' varying vec2 vTexCoord; \n' +
' uniform sampler2D texture1; \n' +
' uniform vec4 uTextColor; \n' +
'void main() { \n' +
'float a = texture2D(texture1,vTexCoord).a;' +
'float r=uTextColor.r*a; \n' +
'float g=uTextColor.g*a; \n' +
'float b=uTextColor.b*a; \n' +
'gl_FragColor = vec4(r,g,b,a); }\n';

This will add new operations for every pixel and we don’t want this. There must be a parameter that we need to change to tell WebGL how to behave with alpha.

Finally I found out, that when you create the context you can write :


   gl = canvas.getContext('webgl',{premultipliedAlpha:false}) ;

However  the colors are still composited with HTML colors used behind so the best way to get an OpenGl behavior is to make alpha false ,this will behave like an OpengGl backbuffer:


gl = canvas.getContext('webgl',{alpha:false}) ;

Hope this helps because I spend  a few hours with WebGl transparency and the solution is super simple! Just one line of code. Cheers

 


blog comments powered by Disqus