Getting Text into a WebGL Texture

Posted on August 12, 2013 | Comments Off

While working on KontikiJS I came across the need to be able to write formatted text into a texture to use with WebGL. You can do this in JavaScript with a bit of work. This is done with the Canvas’s fillText API. The trick is to measure what the text so you can create the Canvas with the right width and height.

Text to WebGL Texture

Here is a demo of what what I am talking about:

There are 2 approaches to measuring the text size. The first is set up the font styling on the Canvas and then call measureText(text). But measureText() returns an object that has the width only. You then use the font size as you height, which is not always correct. Here is some JavaScript that shows what this would look like:

var padding = 12;
var size = 24;
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
context.font = size + "px Georgia";
var textWidth = context.measureText(text).width;
canvas.width = textWidth + padding;
canvas.height = size + padding;

The second method is a bit more complex but can give you a better width and height of the bounding box. This bounding box includes all the styles. To do this what you do is render the text in a hidden div and call clientWidth/clientHeight on the div. Here is what the JavaScript might look like:

var t = textNode = document.createTextNode();
var d = document.createElement("div");
d.appendChild(this.textNode);"position", "absolute");"width", "auto");"height", "auto");"visibility", "hidden");"fontFamily", "Georgia");"fontSize", "24px");
// Need to add it to render
var textWidth = d.clientWidth;
var textHeight = d.clientHeight;

Now that you have the text rendered in a Canvas you just apply that as a texture in WebGL. You’ll want to do any power of 2 canvas adjustments if needed but it is pretty straightforward at this point.

// Three.js example
var texture = new THREE.Texture(canvas);
// WebGL direct example
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas);