Git Product home page Git Product logo

gl-sprite-text's Introduction

gl-sprite-text

experimental

text

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

Tools

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.json

Signed Distance Fields

Bitmap 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.

Static Text

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

Usage

NPM

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:

text = createText(opts)

The following options can be provided:

  • font the bitmap font object, required
  • textures an array of gl textures to match font.paths. If this is not specified, it will look for an images array in the font object, which can be ndarrays, HTMLImage objects, or anything that gets piped to createTexture.
  • text the string of text we will be rendering, default to empty string
  • align a string 'left', 'center', 'right', default left
  • underline boolean, whether to underline the text, default false
  • underlinePosition the position of underline in pixels, defaults to a fraction of font size
  • underlineThickness the underline thickness in pixels, defaults to a fraction of font size
  • lineHeight a line height in pixels, otherwise defaults to an automatic gap
  • letterSpacing the letter spacing in pixels, default 0
  • wrapMode can be normal, pre, or nowrap, default normal
  • wrapWidth an initial number in pixels which is passed to layout() after the other options have been set. Otherwise, defaults to no layout (a single line, no breaks)
  • capacity an initial capacity to use for gl-sprite-batch
  • dynamic whether the WebGL buffers should use DYNAMIC_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.

text.draw(shader[, x, y, start, end])

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.

text.layout([wrapWidth])

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.

text.clearLayout()

Clears the current word-wrapping. This leads to a single line of text, no line-breaks.

text.getBounds()

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.

text.cache([x, y, start, end])

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.

text.uncache()

Disables caching, allowing it to be animated dynamically again.

text.dispose([textures])

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.

License

MIT, see LICENSE.md for details.

gl-sprite-text's People

Contributors

mattdesl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

gl-sprite-text's Issues

drawing 2d-text over a 3d-view

I was wondering whether it is possible to use this module to render 2D-text over a 3D-view? Because I am having problems implementing this.

First of all, I am rendering this 3D bunny:

with_bunny

Now I want to draw some 2D-text on this 3D view. To do this, I imitate the provided demo demo/simple.js, and draw the text using alpha blending. But if I do this, the result is this:

with_text

As can be observed, the bunny disappears for some reason. Here is my code:

'use strict'

/* global requestAnimationFrame */

var shell = require("gl-now")()
var mat4 =require("gl-mat4")
var Geometry = require('gl-geometry')
var normals = require('normals')
var glShader = require('gl-shader')
var bunny = require('bunny')
var glslify = require('glslify')
var vec3 = require('gl-vec3')
var createMovableCamera = require('gl-movable-camera')

var createText = require('gl-sprite-text')
var createBasicShader = require('gl-basic-shader')
var Lato = require('bmfont-lato/32')

var camera = createMovableCamera( {position: vec3.fromValues(-30.0, 12.0, -7.0), viewDir: vec3.fromValues(0.71, -0.21, 0) } );

var shader, bunnyGeom, program

var text, textOrtho=mat4.create(), textTranslate=mat4.create(), textProgram;

shell.on("gl-init", function() {
    var gl = shell.gl;

    gl.enable(gl.DEPTH_TEST)

    /*
    Create buny geometry
     */

    bunnyGeom = Geometry(gl)
    bunnyGeom.attr('aPosition', bunny.positions)
    bunnyGeom.attr('aNormal', normals.vertexNormals(bunny.cells, bunny.positions))
    bunnyGeom.faces(bunny.cells)

    /*
    Load geometry shaders.
     */

    var vertSource = `
precision mediump float;

attribute vec3 aPosition;
attribute vec3 aNormal;

varying vec3 vNormal;

uniform mat4 uProjection;
uniform mat4 uView;

void main() {
  vNormal = aNormal;

  gl_Position = uProjection * uView * vec4(aPosition, 1.0);
}
`;

    var fragSource =
        `
precision mediump float;

varying vec3 vNormal;

void main() {
    vec3 rabbitColor = vec3(0.7);
    // do phong lighting with diffuse and ambient term. 
    gl_FragColor =vec4(0.7 * rabbitColor + dot(vNormal, vec3(0.71, 0.71, 0) ) * rabbitColor, 1.0);
}
`;

    program = glShader(gl, vertSource, fragSource);


    //build our text
    text = createText(gl, {
        font: Lato,
        text: 'Hello, World! Some\nmulti-line text for you.',
        //we can word-wrap like so:
        // wrapWidth: 140
    })

    /*
    Create text shader.
     */
    textProgram = createBasicShader(gl, {
        color: true,
        texcoord: true
    })

})

shell.on("gl-render", function(t) {

    var gl = shell.gl

    gl.clearColor(0.0, 0.4, 0.7, 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);



    /*
     Render geometry
     */

    program.bind()

    var scratch = mat4.create()
    program.uniforms.uProjection = mat4.perspective(scratch, Math.PI/4.0, shell.width/shell.height, 0.1, 1000.0)
    program.uniforms.uView = camera.view()

    bunnyGeom.bind(program)
    bunnyGeom.draw()


    /*
    Render text
     */

    //this is necessary since our image is semi-transparent!
    gl.enable(gl.BLEND)
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)

    mat4.ortho(textOrtho, 0, shell.width, shell.height, 0, 0, 1)

    //gl-basic-shader gives us some matrices we can use
    textProgram.bind()
    textProgram.uniforms.projection = textOrtho

    //get bounds of text after we've adjusted all its params
    var bounds = text.getBounds()

    //here we're translating the text in a shader
    mat4.identity(textTranslate)
    mat4.translate(textTranslate, textTranslate, [10, 10-bounds.y, 0])
    textProgram.uniforms.model = textTranslate



    //Draws from upper-left corner of text box.
    text.draw(textProgram)



    gl.disable(gl.BLEND)
})

Note that if I comment out the line text.draw(textProgram), we get the first image. If I comment it in again, we get the second image.

So what am I doing wrong?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.