I am searching a method to draw better quality (arbitrary) text inside WebGL. Currently I am using bitmap font rendering on a 2D canvas and blitting them into the WebGL context.
After working with Fonts for some time, I can see 6 ways to do fonts in WebGL, all with advantages and disadvantages:
This is also called "On Demand Dynamic Textures". Render only the text glyphs to the (offscreen) canvas and blit it to the screen as a WebGL texture, as described here: http://delphic.me.uk/webgltext.html
The game Age of Empires III uses this method.
If you want max speed with best quality for a limited set of characters and a fixed character size (Game), it is probably best to create your own bitmap featuring the characters you want to use and blit them to the screen as needed. You can find quite a few of these character bitmaps predone on the internet.
This is fast and easy, but limits the languages and characters you can use but you would not mind this in a Game for example.
Chris Green of Valve wrote the "book" on using Signed-Distance Fields for textures. You'll want to read the 2007 SIGGRAPH whitepaper "Improved Alpha-Tested Magnification for Vector Textures and Special Effects
Normally a font texture atlas looks like this. A SDF texture looks like. Yes, the SDF texture atlas looks blurry when rendered "as-is". That's because the 8-bit channel has been encoded as:
smoothstep()
and fwidth()
give poor quality scaled results due to people interpolating in texture space instead of screenspace. It also doesn't help that WebGL's fwidth()
uses the wrong constant.That all said, if you have multiple fonts then SDF fonts can be a huge win in both size (only need 1) and quality (looks fantastic at both small and large sizes)
There is now an easy to use npm module which generates SDF textures and metadata available here.
People are exploring storing the cubic curves on the GPU and bypassing the texture entirely via having a smart fragment shader do all the heavy lifting of rendering.
This blog has a summary of Font Rendering as of February 2017.
For my current project I use an HTML5 2D canvas to render text and other 2D primitives and overlay it using transparency over the WebgGL canvas. I was surprised at the resulting speed, it beats all other methods described here in speed and quality is very good.
As long as your text is static 2D and you do not need any 3D transformations, this would be my recommendation. In my project this is about 2 times faster than the previous method I used (Font as Geometry).
Looking at the source code for three.js suggests a solution.
Here's the code for three.js font creation: FontUtils.js.
It says right at the top:
/*
* For Text operations in three.js (See TextGeometry)
*
* It uses techniques used in:
*
* typeface.js and canvastext
* For converting fonts and rendering with javascript
* https://gero3.github.io/facetype.js/
*
* Triangulation ported from AS3
* Simple Polygon Triangulation
* http://actionsnippet.com/?p=1462
*
* A Method to triangulate shapes with holes
* https://web.archive.org/web/20121018020533/https://www.sakri.net/blog/2009/06/12/an-approach-to-triangulating-polygons-with-holes/
*/
Typeface.js provides the font data and there's an online form to convert truetype fonts.
Other solutions:
Render the text to your bitmaps at a higher resolution and draw them at that resolution or smaller.
Use a curve renderer: Rendering Vector Art on the GPU.
Pixi.js agrees with the recommendation above for SDFs.
https://github.com/PixelsCommander/pixi-sdf-text
They say (and they're an excellent gaming project for the browser)
SDF is the most efficient way to draw text in WebGL. It uses special kind of raster atlases and GLSL shader to draw vector scalable text in a very performant way on GPU.
And this was from MapBox who at least has done it too at some point.