VALIS-software / GPUText

Engine-agnostic WebGL text rendering
MIT License
46 stars 5 forks source link

Question on pipeline of character to rendering #2

Open zumpchke opened 5 years ago

zumpchke commented 5 years ago

I'm new to GPU text. Can you briefly describe how from a given character, the coordinates of the texels are passed to the shader to render?

haxiomic commented 4 years ago

Hey Vanush, sorry about the wait there! So once you've got a font file (like OpenSans-Regular.msdf.bin), you load that as an ArrayBuffer and pass it to GPUText.parse(). This will give you a GPUTextFont object.

A GPUTextFont object is simply the character textures as HTMLImageElements along with a map that describes the location of each character in the map (and kerning information). You should create a GPU texture from the images

Then, when you want to render a string of characters, you first need to layout the characters, for example, in the string hello\nworld, we get a sequence that says character h at position (0,0), character e at position (0.6, 0) and so on, handling kerning, newlines, space characters (and in theory line wrapping). To do this call GPUText.layout( gpuTextFont ) and this gives us a GlyphLayout object

Now we have the text laid out abstractly, we want to generate something we can pass to the GPU. We generate a mesh, where each character has it's own quad and uvs. The character quads are laid out to match the sequence from layout() and the uvs correspond to the character location in the texture map. To generate this run GPUText.generateVertexData( glyphLayout ). The result is a single buffer containing both positions and uvs and layout information to pass to OpenGL that describes how the data is laid out. But generally it's a Float32Array, where each quad vertex has the format p.x, p.y, uv.x, uv.y, uv.z where uv.z is a scale factor.

To render you can use gputext-webgl.js or copy the shader from there and implement in your own engine https://github.com/VALIS-software/GPUText/blob/master/example/gputext-webgl.js#L23

Here's a more advanced implementation used in production https://github.com/VALIS-software/Engine/blob/master/src/ui/Text.ts

The layout part of the code is the least complete – laying out text is a can of worms so I've only provided a minimal implementation (just plain RTL with newlines). You might want to use something like harfbuzz if you need to handle other writing systems or word wrapping

zoldello commented 4 years ago

@haxiomic How is the OpenSans-Regular.msdf.bin file generated?

haxiomic commented 4 years ago

Hey @zoldello, that's generated via the command line utility included with this repo like this:

./cli.js source-fonts/OpenSans/OpenSans-Regular.ttf --binary true

This command relies on another tool called msdfgen, if you're on macos the above command should work out of the box because I've included a prebuilt copy of msdfgen, however if you're on windows or linux you'll need to build this (or download prebuilts from here https://github.com/Chlumsky/msdfgen/releases/tag/v1.8) before running the above command

Place msdfgen.exe into this directory https://github.com/VALIS-software/GPUText/tree/master/generate/prebuilt

zoldello commented 4 years ago

@haxiomic Does this exception make sense to your (running on mac): (base) saccharo:GPUText phil$ ./cli.js '../../../../Downloads/Mada-Regular_2.ttf' ./ --binary true

Error: ENOENT: no such file or directory, open 'charsets/ascii.txt'

Usage: [options]

_ : Path of TrueType font file (.ttf) ['--charset'] : Path of file containing character set ['--charlist'] : List of characters ['--output-dir', '-o'] : Sets the path of the output font file. External resources will be saved in the same directory ['--technique'] : Font rendering technique, one of: msdf, sdf, bitmap ['--msdfgen'] : Path of msdfgen executable ['--size'] : Maximum dimension of a glyph in pixels ['--pxrange'] : Specifies the width of the range around the shape between the minimum and maximum representable signed distance in pixels ['--max-texture-size'] : Sets the maximum dimension of the texture atlas ['--bounds'] : Enables storing glyph bounding boxes in the font (default false) ['--binary'] : Saves the font in the binary format (default true) ['--external-textures'] : Store textures externally when saving in the binary format ['--help'] : Shows this help

(base) saccharo:GPUText phil$

haxiomic commented 4 years ago

Ahh sorry it’s a while since I last ran this - it’s looking for a character set file (which is just a text file containing all the character you want to support)

This file is contained inside generate/charsets, along with some others to choose from

If you add --charset generate/charsets/ascii.txt it should now work

I've updated the tool so it now uses ASCII by default and doesn't look for this text file (d0f73831247107a17171c8bbaa7c63a116f592d9)