Search code examples
canvastexttextureswebgldynamically-generated

generating text texture in webgl: Alpha is opaque


I'm generating a texture from a canvas that contains text, the problem is that the transparent part of the text is completely ignored when I render the texture.

comparision

When I render the texture it looks like the top text, when it should look like the bottom text.

This is how I generate the texture:

canvas.width = textRendBuffC.measureText(text).width*2;
canvas.height = parseInt(fontStyle, 10)*1.5;
c.clearRect(0, 0, canvas.width, canvas.height);
c.font = fontStyle;
c.fillStyle = colorStyle;
c.fillText(text, 0, parseInt(fontStyle, 10));


var canvasTexture = gl.createTexture();

gl.bindTexture(gl.TEXTURE_2D, canvasTexture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL,true);

gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas);

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);

And this is how I draw the texture:

gl.bindBuffer(gl.ARRAY_BUFFER, spritePlan.vertBuffer);
gl.vertexAttribPointer(currentShader.sh.vertexAttribLocation, spritePlan.vertBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, spritePlan.texCoordBuffer);
gl.vertexAttribPointer(currentShader.sh.textureCoordAttribute, spritePlan.texCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, spritePlan.colorBuffer);
gl.vertexAttribPointer(currentShader.sh.colorAttribLocation, spritePlan.colorBuffer.itemSize, gl.FLOAT, false, 0, 0);

gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.uniform1i(currentShader.sh.sampler2DUniform, 0);

gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, spritePlan.indexBuffer);
setMatrixUniforms();

gl.drawElements(gl.TRIANGLES, spritePlan.indexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
gl.bindTexture(gl.TEXTURE_2D, null);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);

And this is the code the fragment shader goes through:

vec4 tex = texture2D(sampler2DUniform, vTextureCoord);
gl_FragColor = vec4(tex.rgb, tex.a);

Any ideas?


Solution

  • You have to set the UNPACK_PREMULTIPLY_ALPHA_WEBGL paramter to true (pixelStorei). This will cause the the color channels to be multiplied by the alpha channel, The initial value is false:

    gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
    

    See WebGL Fundamentals - textures:

    This tells WebGL to supply premultiplied alpha values to gl.texImage2D and gl.texSubImage2D. If the data passed to gl.texImage2D is already premultiplied as it is for Canvas 2D data then WebGL can just pass it through.

    See Khronos specification Premultiplied Alpha, Canvas APIs and texImage2D:

    When passing a WebGL-rendered Canvas to the texImage2D API, then depending on the setting of the premultipliedAlpha context creation parameter of the passed canvas and the UNPACK_PREMULTIPLY_ALPHA_WEBGL pixel store parameter of the destination WebGL context, the pixel data may need to be changed to or from premultiplied form.


    Further, you set up the texture paramter TEXTURE_MAG_FILTER with NEAREST:

    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER,gl.NEAREST);
    

    See the Khronos OpenGL reference for Sampler Object:

    The magnification filter is controlled by the GL_TEXTURE_MAG_FILTER texture parameter. This value can be GL_LINEAR or GL_NEAREST. If GL_NEAREST is used, then the implementation will select the texel nearest the texture coordinate; this is commonly called "point sampling". If GL_LINEAR is used, the implementation will perform a weighted linear blend between the nearest adjacent samples.

    This means, if you use NEAREST, then the the nearest texel is read from the texture, according to the texture coordinates. This causes a hard and angular appearance.
    If you set the texture parameter TEXTURE_MAG_FILTER to LINEAR, the texels will be interpolated and the weighted average of the 4 pixels surrounding the given coordinate is calculated. The result is a smoother appearance:

    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER,gl.LINEAR);
    


    See also: