SDF vs MSDF vs Slug vs Rive vs Texture Atlas: GPU Text Rendering Explained
Pangram verdict · v3.3
We believe this text is mainly AI, with some human-written content.
AI likelihood · overall
AIArticle text · 1,546 words · 1 segments analyzed
How do I choose a text rendering algorithm between SDF, MSDF, Slug, Texture Atlas or Rive? Text looks simple until you have to draw it yourself. A letter is not a picture, it is a set of outlines: closed loops of straight lines and Bezier curves, filled according to a winding rule. Drawing that on a CPU into a bitmap is a solved problem. Drawing it on a GPU, crisply, at any size, under any 3D transform, while the text changes every frame, is not. Most engines dodge the hard version by baking glyphs into textures ahead of time and living with the compromises. In 2017 Eric Lengyel published an algorithm, called Slug, that stopped dodging. It renders glyphs directly from their outlines in the fragment shader, with no texture atlas and no per-frame tessellation. Lengyel patented it in 2019, and on March 17, 2026 he dedicated that patent to the public domain. That is why we built Slughorn, our C++20 implementation of the Slug technique, and it is why we can now talk about how it works and where it wins. This is a tour of how GPU text rendering actually works, from the bitmap atlas up to Slug, and where each method fits. What makes glyphs hard Every scalable font stores each glyph as vector outlines. TrueType uses quadratic Bezier curves, OpenType with CFF uses cubic curves, and both mix in straight segments. The interior of the letter is whatever the fill rule says is inside, usually the nonzero winding rule: shoot a ray from the pixel, count how the outline crosses it, and if the winding number is nonzero the pixel is inside the glyph. A glyph is a set of outlines, not pixels: filled dots are on-curve points, open circles are Bezier control points. The renderer has to do three things at once and do them fast: fill the interior correctly, produce clean antialiased edges, and stay sharp whether the glyph is 8 pixels tall in a menu or filling the screen on a billboard rotated in perspective. On a CPU you rasterize each glyph once at its target size and you are done. On a GPU you want to draw thousands of glyphs per frame, at arbitrary scales, ideally without re-rasterizing anything. That constraint is where every technique below makes its trade. Method 1: the texture atlas (bitmap glyphs) The oldest and still most common approach. Rasterize each glyph once, at one size, into a shared texture called an atlas, then draw each on-screen character as a textured quad that samples its slot. It is fast, trivially portable, and runs on anything with a texture unit. That is why it is everywhere. The problems show up the moment you scale. Enlarge past the baked size and the glyph turns into blurry or blocky pixels, because you are magnifying a bitmap. Shrink it and you get shimmer and dropped stems unless you bake mip levels. Every size you want crisp is another atlas. Every language is another problem: a Latin atlas is small, but Chinese, Japanese, and Korean have tens of thousands of glyphs, and baking all of them at several sizes is a memory disaster. And a bitmap has no idea it is being viewed in perspective, so text laid onto a 3D surface looks soft. Magnifying a baked atlas glyph (left and center) versus rendering it from the outline (right). Method 2: signed distance fields (SDF) Valve introduced the fix that carried the industry for a decade. Chris Green's 2007 SIGGRAPH work, "Improved Alpha-Tested Magnification for Vector Textures and Special Effects," stores not the glyph's pixels but a signed distance field: each texel holds the distance to the nearest edge, positive inside, negative outside. In the shader you sample that field and threshold at zero. Because distance interpolates smoothly, you can scale a small SDF texture up dramatically and still get a clean edge, and you get cheap antialiasing by softening the threshold. One small texture, resolution independent within reason, one cheap shader. For a long time this was the default for crisp UI text and game HUDs, and it still is on constrained hardware. But an SDF is still a baked texture sampled at a fixed resolution, and it lies about corners. A sharp corner is a discontinuity in the distance field, and bilinear interpolation rounds it off. Every hard corner on a letter, the point of an "A", the notch of a "K", gets softened. Push the magnification far enough, or make the glyph small enough that the field is only a few texels wide, and thin stems break up and detail smears. A signed distance field (left) and the crisp edge a shader recovers from it (right). Method 3: multi-channel signed distance fields (MSDF) Viktor Chlumsky's work, from his 2015 thesis and the 2018 paper "Improved Corners with Multi-Channel Signed Distance Fields," fixes the corner problem. Instead of one distance channel, MSDF stores three, in red, green, and blue, each encoding distance to a different subset of edges chosen so that sharp corners survive. In the shader you take the median of the three channels. The median trick reconstructs corners almost perfectly, so an MSDF glyph stays crisp at magnifications that would round an SDF to mush. MSDF is the current sweet spot for a lot of teams, and Chlumsky's msdfgen is MIT licensed and widely adopted. If you need crisp scalable text and you are willing to bake an atlas, it is an excellent choice. It is still an atlas, though, with the costs that implies. You bake each glyph at a chosen resolution ahead of time, so dynamic or user-supplied text, and enormous glyph sets like CJK, still mean baking pipelines and memory budgets. Generation is more expensive than plain SDF. At very small sizes you are still sampling too few texels to hold fine detail, and at extreme minification you still fight aliasing. The three-channel lookup costs more bandwidth than one. MSDF raises the ceiling on quality, but it still uses an atlas. SDF rounds sharp corners; MSDF preserves them. Images: Viktor Chlumsky / msdfgen (MIT). Method 4: tessellation and coverage (Loop-Blinn, NV_path_rendering, Pathfinder, Rive) A different family skips textures entirely and turns the outline into geometry the GPU can rasterize. Loop-Blinn feeds curved triangles to the GPU and uses a per-pixel discard shader to keep only the inside of each quadratic Bezier, combined with the stencil buffer to resolve winding. Elegant, but reliable antialiasing is genuinely hard without tricks that cost quality. Stencil-then-cover, exposed as NVIDIA's NV_path_rendering extension, draws the path into the stencil buffer in one pass, then covers it in a second. It is high quality but leans on vendor extensions and specific hardware paths. Pathfinder tessellates edges into microtriangles, computes signed trapezoidal areas per pixel, and accumulates coverage in a compute pass. Tile-based and fast on modern GPUs. Rive's renderer, open-sourced in 2024, reduces antialiased vector paths into unique triangle patches and rasterizes them through a massively parallel pipeline with pixel local storage, hitting 120 fps on animated vector art. This family is genuinely resolution-independent and, for animated designed vector graphics, often the right answer. Rive in particular is built for artwork that moves. The costs are the tessellation itself, which has to be redone when geometry changes, the geometry blowup for complex glyphs, the difficulty of clean analytic antialiasing, and in some cases a dependence on specific hardware features or extensions. Tessellation methods turn the outline into triangles the GPU rasterizes. Method 5: Slug, rendering straight from the outline Slug skips the atlas and per-frame tessellation and keeps the glyph as a list of quadratic Bezier curves and line segments stored in a small GPU buffer. Alongside it, Slug builds a lightweight per-glyph acceleration structure that partitions the glyph into horizontal bands, so a given pixel only has to consider the handful of curves near it rather than the whole outline. Then it resolves coverage directly in the fragment shader. For each pixel it effectively casts a ray, finds where that ray crosses the nearby Bézier curves, and counts those crossings to compute the winding number and therefore coverage. The hard part, and Lengyel's secret sauce, is a test he calls root eligibility: a precise rule for which curve-ray intersections should count, so the winding math is exact at the shared endpoints where curves meet and where naive approaches produce cracks or double-counts. Because the shader is solving the curve equations analytically rather than sampling a baked field, it produces exact coverage and clean antialiasing at any scale. Slug’s core idea: for each pixel, cast a ray and count how many times it crosses the outline. There is no baked resolution, so the same glyph is razor sharp at 6 pixels or 6000, and it stays sharp under arbitrary 2D and 3D transforms, including perspective, because coverage is computed per pixel after the transform. There is no atlas, so a hundred thousand CJK glyphs cost a font's worth of outline data, not an atlas the size of a video. Text can change every frame at no baking cost, which is exactly what you want for live data, user input, and localized content. And it all happens in a single draw with an ordinary fragment shader, no vendor extension required.