DEV Community

Andrei Lesnitsky
Andrei Lesnitsky

Posted on

WebGL Month. Day 30. Text rendering in WebGL

This is a series of blog posts related to WebGL. New post will be available every day

GitHub stars
Twitter Follow

Join mailing list to get new posts right to your inbox

Soruce code available here

Built with

Git Tutor Logo


Hey πŸ‘‹

Welcome to WebGL month.

In previuos tutorials we were focused on rendering 2d and 3d shapes, but never rendered text, which is important part of any application.

In this article we'll review possible ways of text rendering.

HTML overlay

The most obvoius and simple solution would be to render text with HTML and place it above the webgl canvas, but this will only work for 2D scenes, 3D stuff will require some calculations to calculate text position and css transforms

Canvas as texture

Other technique might be applied in a wider range of cases. It requires several steps

  1. create another canvas
  2. get 2d context (canvas.getContext('2d'))
  3. render text with fillText or strokeText
  4. use this canvas as webgl texture with correct texture coordinates

Since the texture is a rasterized image, it will loose the quality when you'll come "closer" to the object

Glyphs texture

Each font is actually a set of "glyphs" – each symbol is rendered in a signle image

A | B | C | D | E | F | G |
---------------------------
H | I | J | K | L | M | N |
...

Each letter will have it's own "properties", like width (i is thiner than W), height (o vs L) etc.
These properties will affect how to build rectangles, containing each letter

Typically aside of texture you'll need to have a javascript object describing all these properties and coordinates in original texture image

const font = {
    textureSize: {
        width: 512,
        height: 512,
    },
    height: 32,
    glyphs: {
        a: { x: 0, y: 0, height: 32, width: 16 },
        b: { x: 16, y: 0, height: 32, width: 14 },
    },
    // ...
};

and to render some text you'll need something like this

function getRects(text, sizeMultiplier) {
    let prevLetterX = 0;

    const rects = text.split('').map((symbol) => {
        const glyph = font.glyphs[symbol];

        return {
            x: prevLetterX,
            y: font.height - glyph.height,
            width: glyph.width * sizeMultiplier,
            height: glyph.height * sizeMultiplier,
            texCoords: glyph,
        };
    });
}

Later this "rects" will be used to generate attributes data

import { createRect } from './gl-helpers';

function generateBuffers(rects) {
    const attributeBuffers = {
        position: [],
        texCoords: [],
    };

    rects.forEach((rect, index) => {
        attributeBuffers.position.push(...createRect(rect.x, rect.y, rect.width, rect.height)),
            attributeBuffers.texCoords.push(
                ...createRect(rect.texCoords.x, rect.texCoords.y, rect.texCoords.width, rect.texCoords.height)
            );
    });

    return attributeBuffers;
}

There's a gl-render-text package which can render texture based fonts

Font triangulation

Since webgl is capable of drawing triangles, one more obvious solution would be to break each letter into triangles
This seem to be a very complex task 😒

Luckily – there's a fontpath-gl package, which does exactly this

Signed distance field font

Another technique for rendering text in OpenGL/WebGL

Find more info here


GitHub stars
Twitter Follow

Join mailing list to get new posts right to your inbox

Soruce code available here

Built with

Git Tutor Logo

Top comments (0)