SDF vs. MSDF vs. Slug: GPU Text Rendering
SDF vs. MSDF vs. Slug: GPU Text Rendering
By Chris Hanson | Posted on September 23, 2026 作者:Chris Hanson | 发布于 2026 年 9 月 23 日
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. 在 SDF、MSDF、Slug、纹理图集(Texture Atlas)或 Rive 之间,我该如何选择文本渲染算法?在需要亲自实现之前,文本看起来很简单。字母不是图片,而是一组轮廓:由直线和贝塞尔曲线组成的闭合回路,并根据填充规则进行填充。在 CPU 上将其绘制为位图是一个已经解决的问题。但在 GPU 上,要在任何尺寸、任何 3D 变换下,且文本每帧都在变化的情况下,清晰地绘制出文本,却并非易事。
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. 大多数引擎通过预先将字形烘焙到纹理中来规避这一难题,并接受由此带来的妥协。2017 年,Eric Lengyel 发布了一种名为 Slug 的算法,不再回避这一问题。它直接在片元着色器(fragment shader)中根据轮廓渲染字形,无需纹理图集,也无需每帧进行细分(tessellation)。Lengyel 在 2019 年为其申请了专利,并于 2026 年 3 月 17 日将该专利贡献给了公共领域。这就是我们构建 Slughorn(我们对 Slug 技术的 C++20 实现)的原因,也是我们现在可以讨论其工作原理及其优势的原因。本文将带您了解 GPU 文本渲染的实际工作方式,从位图图集到 Slug,以及每种方法的适用场景。
What makes glyphs hard
为什么字形渲染很困难
Each 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. 每种可缩放字体都将每个字形存储为矢量轮廓。TrueType 使用二次贝塞尔曲线,带有 CFF 的 OpenType 使用三次曲线,两者都混合了直线段。字母的内部由填充规则决定,通常是非零环绕规则(nonzero winding rule):从像素点发射一条射线,计算轮廓与射线的交点,如果环绕数(winding number)不为零,则该像素位于字形内部。
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. 字形是一组轮廓,而不是像素:实心点是曲线上的点,空心圆是贝塞尔控制点。渲染器必须同时快速完成三件事:正确填充内部、产生清晰的抗锯齿边缘,并确保无论是在菜单中 8 像素高的字形,还是在透视旋转的广告牌上填满屏幕的字形,都能保持清晰。在 CPU 上,你只需按目标尺寸光栅化每个字形一次即可。而在 GPU 上,你希望每帧绘制数千个字形,且支持任意缩放,理想情况下无需重新光栅化。这一约束正是以下每种技术进行权衡的出发点。
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. 这是最古老且至今仍最常用的方法。将每个字形按单一尺寸光栅化一次,存入名为“图集”的共享纹理中,然后将屏幕上的每个字符绘制为采样该槽位的纹理四边形。它速度快、易于移植,且能在任何带有纹理单元的设备上运行。这就是它无处不在的原因。但当你进行缩放时,问题就出现了。放大超过烘焙尺寸后,字形会变成模糊或块状的像素,因为你是在放大位图。缩小它则会导致闪烁和笔画丢失,除非你烘焙了多级渐进纹理(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. 你想要清晰的每一种尺寸都需要一个额外的图集。每种语言也是一个难题:拉丁语图集很小,但中日韩文字有数万个字形,将它们全部按多种尺寸烘焙会造成内存灾难。此外,位图无法感知透视效果,因此贴在 3D 表面上的文本看起来会很模糊。
Method 2: signed distance fields (SDF)
方法二:有向距离场(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 antialiased by softening the threshold. Valve 引入的解决方案引领了行业十年。Chris Green 在 2007 年 SIGGRAPH 上发表的论文《Improved Alpha-Tested Magnification for Vector Textures and Special Effects》中,不再存储字形的像素,而是存储有向距离场:每个纹素(texel)存储到最近边缘的距离,内部为正,外部为负。在着色器中,你对该场进行采样并以零为阈值进行判断。由于距离可以平滑插值,你可以将较小的 SDF 纹理大幅放大,仍能获得清晰的边缘,并且通过软化阈值可以低成本地实现抗锯齿。
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. 一张小纹理,在合理范围内与分辨率无关,且着色器开销很小。长期以来,这是清晰 UI 文本和游戏 HUD 的默认方案,在受限硬件上依然如此。但 SDF 本质上仍是固定分辨率下采样的烘焙纹理,它在处理拐角时会失真。尖锐的拐角在距离场中是不连续的,而双线性插值会将其圆滑化。字母上的每一个硬拐角,如“A”的尖端、“K”的缺口,都会被软化。如果放大倍数过大,或者字形小到距离场只有几个纹素宽,细笔画就会断裂,细节也会变得模糊。