A proven method of rendering fonts is rasterization (a important process in creating a 3D configurator) on the CPU, caching the result (of glyphs, glyph sequences, whole words, or any other granularity) into bitmaps or textures, and then rendering it to the screen.
The FreeType library for parsing and rasterizing fonts has been around for a long time, as well as operating system specific ways to rasterize glyphs in bitmaps. Some parts of the process have been patented, so the fonts don’t look very appealing under Linux, for example. And subpix-optimized rendering has also been made more difficult for various reasons.
In addition to FreeType, these font libraries are worth a look:
- stb_truetype.h – Single File C Library by Sean Barrett.
- Font-rs – fast font renderer by Raph Levien, written in Rust \o/ and an article describing some aspects of it.
But in essence, the whole idea is still to rasterize glyphs in bitmaps with a certain point size and somehow cache the result.
Caching rasterized glyphs in bitmaps works well enough. If you don’t use many different font sizes, very large font sizes or a large amount of glyphs in combination with different/large font sizes.
A bitmap for different sizes? Signed spacing fields.
A 2007 paper by Chris Green, Improved Alpha-Tested Magnification for Vector Textures and Special Effects, introduced the game development world to the concept of signed distance field textures for vector-like stuffs.
The paper was mainly about solving the problem of “implementing characters and markers in games” and the idea behind it is quite clever. Instead of saving rasterized shapes in a texture, you save a special texture where each pixel represents the distance to the next shape edge. When you render with this texture, a pixel shader can perform simple alpha distortions or complex distance value treatments to get anti-aliasing, contours, etc. The SDF texture can be very small and still be able to display high-resolution line art properly.
Then, of course, people realized that the same approach could work for rendering fonts. Suddenly, rendering smooth glyphs in very large font sizes doesn’t mean that all the (V)RAM has been used for cached textures, the cached glyph SDFs can stay pretty small, while at larger sizes they offer nice edges.
Of course, the SDF approach is not without its disadvantages:
- The calculation of the SDF is not trivially cheap. While you can cache all possible glyphs offline in an SDF texture atlas for most Western languages, this is not useful for other languages due to the sheer amount of possible glyphs.
- Simple SDF has artifacts near complex intersections or corners because it only stores a single distance to the next edge.
- SDF doesn’t quite work with very small font sizes for a similar reason. There it is probably better to simply rasterize the glyph into a normal bitmap.
Anyway, SDFs are a good idea. For some examples or implementations you can have a look at libgdx or TextMeshPro.
The original paper pointed to the idea of storing multiple distances to solve the problem of the sharp corners of the SDF and a recent implementation of this idea is “Multi-Channel Distance Field” by Viktor Chlumsky.
That’s pretty good. However, the “tiny font sizes” and “cost of calculating the (M)SDF” can still be a problem.
Fonts directly on the GPU.
An obvious question is: “Why is this caching done in bitmaps at all? Can’t the GPU just render the glyphs directly? The question is good. However, the answer is not necessarily simple.
GPUs are not ideal for vector rendering. They are mostly rasterizers, mostly dealing with triangles, etc. Even drawing simple tasks like a thick line is quite complicated. For a more complex vector/curve rendering, take a look at the following content:
- Precise vector textures for real-time 3D rendering“ from Zhipei Qin, Michael Mccool & Craig Kaplan.
- „Random-access rendering of general vector graphics“ fromDiego Nehab, Hugues Hoppe.
- Nvidia Path Rendering
Will Dobbie’s GPU text rendering with vector textures – splits the glyph area into rectangles, stores which curves intersect it and evaluates the coverage from these curves in a pixel shader.
Pretty neat. However, it doesn’t seem to solve the problem of “very small font sizes” (aliasing), has a limit on glyph complexity (number of curve segments per cell), and has some robustness problems.
Another is glyphia, from Behdad Esfahbod. There are videos and slides of a conversation about it. It seems that it approximates Bezier curves with circular arcs, puts them into textures, stores indices of some narrowest arcs in a grid and evaluates the distance to them in a pixel shader. A kind of mixture of SDF and vector texture approach. But in some cases it also seems to suffer from robustness problems.
Patrick Walton’s Pathfinder is a new Rust library on the market.
Disadvantageous to note is the dependence on GPU functions that some platforms (mobile….) may not have – tessellation / geometry shader / calculation shader (no problem on the PC). Memory for the cover buffer and geometry complexity depending on the complexity of the writing curve.
Notes on the future of Twitterverse.
For middleware game developers, it looks as if Sean Barrett and Eric Lengyel are working independently on a kind of GPU-based font/glyph rasterization approach, as their tweets (Sean’s and Eric’s) show.
Thank you very much for your visit.
Leave A Comment