Skip to main content

Fonts

Loading...

There are several approaches to render text in WebGL as described here. NiiVue uses multi-channel signed distance field fonts. As seen in this demo, NiiVue provides several in-built fonts and provides a mechanism to scale the font. You can also generate custom fonts, which may be especially important for supporting languages that do not use the Latin alphabet. The default font used by NiiVue is Roboto, created with msdf-atlas-gen using the command:

msdf-atlas-gen.exe -font Roboto-Regular.ttf -charset chars.txt -pxrange 2 -dimensions 512 256 -format png -json fnt.json -imageout fnt.png

Where chars.txt is a text file with the following characters

"\"\\ ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890!`?'.,;:()[]{}<>|/@^$-%+=#_&~*"

Running the command will generate output

Loaded geometry of 95 out of 95 characters.
Glyph size: 59.65625 pixels/EM
Atlas image file saved.
Glyph layout and metadata written into JSON file.

The typeface or character set can be changed by modifying the commands. NiiVue will read the JSON format created by msdf-atlas-gen (version 1.1), so to change the typeface used by NiiVue, simply replace the fnt.json and fnt.png files in public folder.

Usage

Viktor Chlumský's GLSL fragment shader is easily adapted for WebGL. There are many parameters you can tweak. A core parameter is the uniform screenPxRange. Viktor describes this as the distance field range in output screen pixels. For example, if the pixel range was set to 2 when generating a 32x32 distance field, and it is used to draw a quad that is 72x72 pixels on the screen, it should return 4.5 (because 72/32 * 2 = 4.5). Note that both distance range and distance field are reported in the JSON file created by msdf-atlas-gen: "distanceRange":2,"size":59.65625. Therefore, the complete WebGL fragment shader is:

#version 300 es
precision highp int;
precision highp float;
uniform highp sampler2D fontTexture;
uniform vec4 fontColor;
uniform float screenPxRange;
in vec2 vUV;
out vec4 color;
float median(float r, float g, float b) {
return max(min(r, g), min(max(r, g), b));
}
void main() {
vec3 msd = texture(fontTexture, vUV).rgb;
//color = vec4(msd, 1.0); return;
float sd = median(msd.r, msd.g, msd.b);
float screenPxDistance = screenPxRange*(sd - 0.5);
float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);
color = vec4(fontColor.rgb , opacity);
}