A solution for bitmap and SDF text rendering in stack.gl. This uses gl-sprite-batch and fontpath under the hood.
The BMFont spec is used for glyph and font data. You also need to pass an array of gl-texture2d items matching the paths specified by the font file. (Multi-page fonts are supported.)
The font object can also include an images array (ndarray/HTMLImage), which will get piped into gl-texture2d. You can use bmfont-lato for testing; it includes Lato Regular in a few sizes and the base64-inlined images ndarray.
var createText = require('gl-sprite-text')
var Lato = require('bmfont-lato')
//build the text
text = createText(gl, {
font: Lato,
text: 'Hello, World!'
})
//optionally word-wrap it to a specific width
text.layout(500)
function render() {
//draws the text with lower-left origin
text.draw(shader)
}See demo/simple.js for an example. After npm install, you can run it with:
npm run demo-simple
After you've exported the BMFont with your favourite tool, you can run it through bmfont2json to produce valid output:
# if you haven't already, install the tool globally
npm install bmfont2json -g
# then you can use it like so..
bmfont2json Lato32.fnt > Lato32.jsonBitmap fonts are great for fixed-size text, but if you need large fonts, or fonts that scale smoothly (i.e. if it's being 3D transformed), it's better to use alpha testing to avoid aliasing artifacts. To generate SDF font atlases, you can use Hiero and LibGDX. Then, you need to render it with a signed distance field shader. See the demo/sdf.js example:
npm run demo-sdf
As you can see from the demo, you can also achieve drop shadows, outlines, glows and other effects with independent colors.
By default, the text is pushed to dynamic buffers every frame. This allows it to animate (e.g. changing position, start/end indices, text content), and also ensures that underlines and multi-page textures will work.
Basic static text is supported with the cache() method. Static text only supports a single texture page, and no underlines.
var text = createText(gl, {
font: myFont,
textures: textures,
text: str,
//hint to buffers that they will be static
dynamic: false
})
//cache the current text state
text.cache(x, y, start, end)
function render() {
text.draw(shader)
}Inherits from fontpath-simple-renderer so the API should work, but this module may diverge from it in the future. Here is the current public API:
The following options can be provided:
fontthe bitmap font object, requiredtexturesan array of gl textures to matchfont.paths. If this is not specified, it will look for animagesarray in thefontobject, which can be ndarrays, HTMLImage objects, or anything that gets piped tocreateTexture.textthe string of text we will be rendering, default to empty stringaligna string 'left', 'center', 'right', default leftunderlineboolean, whether to underline the text, default falseunderlinePositionthe position of underline in pixels, defaults to a fraction of font sizeunderlineThicknessthe underline thickness in pixels, defaults to a fraction of font sizelineHeighta line height in pixels, otherwise defaults to an automatic gapletterSpacingthe letter spacing in pixels, default 0wrapModecan benormal,pre, ornowrap, defaultnormalwrapWidthan initial number in pixels which is passed tolayout()after the other options have been set. Otherwise, defaults to no layout (a single line, no breaks)capacityan initial capacity to use for gl-sprite-batchdynamicwhether the WebGL buffers should useDYNAMIC_DRAW, default true
All options except for font, wrapMode and wrapWidth are fields which be changed at runtime, before calling draw().
Note: Changing the text currently calls clearLayout(). You will need to call layout() again.
Draws the text with the given shader, at the specified pixel position (lower-left origin).
The start (inclusive) and end (exclusive) indices will draw the laid out glyphs within those bounds. This can be used to style and colour different pieces of text. If not specified, they will default to 0 and the text length, respectively.
If text is cached, the x, y, start, end parameters are ignored.
Word-wraps the text with the current wrap mode to the optional given width. You can change the wrap mode like so:
text.wordwrap.mode = 'pre'
text.layout(250)If no width is specified, it will only break on explicit newline characters \n.
This creates some new objects in memory, so you may not want to do it every frame.
Clears the current word-wrapping. This leads to a single line of text, no line-breaks.
Returns an object with the computed bounds of the text box:
{ x, y, width height }
This can be used to draw the text at an upper-left origin instead.
Caches the current text parameters into a static buffer. Underlines are not supported; and this only works with one texture page (e.g. all glyphs in a single sprite sheet).
The parameters replace those in draw(). When cached, draw() will ignore the x, y, start, end parameters.
Disables caching, allowing it to be animated dynamically again.
If no batch was provided during the constructor, this will dispose of the default (internally created) batch.
Specifying true for textures (default false) will also dispose of the texture array associated with this text object.
MIT, see LICENSE.md for details.
