文本看起来简单至极,直到你必须在 GPU 上绘制它。字形不是位图;它是一组封闭的矢量轮廓——二次或三次 Bézier 曲线加上直线段——由绕组规则填充。在 CPU 上,你在目标尺寸下栅格化一次就完成了。在 GPU 上,你希望每帧渲染数千个字形且尺度任意,理想情况下无需重新栅格化任何内容。正是这种约束让每种技术做出各自的权衡。

文章开头解释字形是一组由绕组规则填充的封闭矢量轮廓,而非位图。本图通过标记曲线上的点与曲线外的点来说明这一基本概念。
**1. 纹理图集(位图字形)** 最古老且至今最常见的方法。将每个字形在单一尺寸下栅格化一次,存入共享的纹理图集,然后将每个字符绘制为带纹理的四边形。它速度快、可移植性极佳,且能在任何带纹理单元的设备上运行。问题在你缩放的那一刻便会出现:放大超过烘焙尺寸,字形就会模糊;缩小它,除非烘焙 mipmap 级别,否则字干会闪烁或消失。每个清晰的尺寸都需要另一个图集。对于中文、日文和韩文,数以万计的字形,烘焙多个尺寸会变成一场内存灾难。而且位图没有透视概念,所以 3D 表面上的文本看起来很模糊。
**2. 有符号距离场(SDF)** Valve 的 Chris Green 在 SIGGRAPH 2007 上引入了 SDF。每个纹素不存储像素,而是存储到最近边缘的有符号距离(内部为正,外部为负)。在着色器中,你采样该场并在零处阈值化。因为距离可以平滑插值,一个小的 SDF 纹理可以以干净边缘大幅放大,且廉价的抗锯齿来自柔化阈值。十年来,这一直是清晰 UI 和 HUD 文本的默认方案。
但 SDF 仍然是固定分辨率下的烘焙纹理,而且它在角落上并不准确。一个尖锐的角是距离场中的一个不连续点;双线性插值会把它圆化。每一个硬角——“A”的尖端、“K”的凹口——都会被柔化。把放大推得足够远,或者把字形做得足够小以至于该场只有几个纹素宽,细字干就会碎裂,细节会晕开。
**3. 多通道 SDF(MSDF)** Viktor Chlumsky 2015 年的论文和 2018 年的论文解决了角落问题。MSDF 不存储一个距离通道,而是存储三个(红、绿、蓝),每个通道编码到不同边缘子集的距离,这些边缘子集的选择使得尖锐角落得以保留。着色器取三个通道的中位数,几乎完美地重建角落。MSDF 是许多团队当前的甜点;Chlumsky 的 `msdfgen` 采用 MIT 许可并被广泛使用。
它仍然是图集。你预先以选定的分辨率烘焙每个字形,因此动态或用户提供的文本以及像 CJK 这样巨大的字形集仍然意味着烘焙管线和内存预算。生成比普通 SDF 更昂贵。在非常小的尺寸下,你采样的纹素太少,无法保留精细细节;在极端缩小下,你仍然要与锯齿抗争。三通道查找消耗更多带宽。MSDF 提高了质量上限,但保留了图集约束。
**4. 细分与覆盖(Loop–Blinn、NV_path_rendering、Pathfinder、Rive)** 这一家族完全跳过纹理,将轮廓转化为 GPU 可以栅格化的几何体。Loop–Blinn 向 GPU 提供带逐像素丢弃着色器和模板绕组的曲面三角形。NV_path_rendering 使用两遍的先模板后覆盖方法。Pathfinder 将边缘细分为微三角形,并在一次计算通道中累积覆盖。Rive 的渲染器(2024 年开源)将抗锯齿矢量路径简化为唯一的三角形补丁,并通过具有像素局部存储的大规模并行管线进行栅格化,在动画矢量艺术上达到 120 fps。
这些方法确实与分辨率无关,对于动画设计的矢量图形,它们往往是正确的答案。代价是细分本身(在几何体变化时重新做)、复杂字形的几何体膨胀、干净的解析抗锯齿的困难,以及在某些情况下对特定硬件特性或扩展的依赖。Rive 特别是只接受 2D 仿射变换;透视投影不是仿射的,因此它最好的近似在中心是精确的,并向边缘漂移。
**5. Slug:直接从轮廓渲染** Eric Lengyel 于 2017 年发布了 Slug,2019 年获得专利,并于 2026 年 3 月 17 日将该专利奉献给公共领域。Slug 跳过图集和逐帧细分。它将字形以二次 Bézier 曲线和线段列表的形式保存在小型 GPU 缓冲区中,同时伴随一个轻量的逐字形加速结构,将字形划分为水平带,使得给定像素只需考虑其附近的少量曲线。
覆盖直接在片段着色器中解析。对于每个像素,着色器实际上投射一条射线,找到该射线与附近 Bézier 曲线的交叉点,并统计这些交叉点以计算绕组数,从而得到覆盖。Lengyel 的关键创新是一个*根资格*测试:一条精确的规则,决定哪些曲线-射线交叉应该被计入,使得在曲线相接的共享端点处绕组数学是精确的,而朴素方法会在这些地方产生裂缝或重复计数。因为着色器解析地求解曲线方程,而不是采样烘焙的场,它在任何尺度下都能产生精确的覆盖和干净的抗锯齿。
没有烘焙分辨率,因此同一个字形在 6 像素或 6,000 像素时都锋利如剃刀,并且在任意 2D 和 3D 变换下保持锋利,包括透视,因为覆盖是在变换*之后*逐像素计算的。没有图集,所以十万个 CJK 字形只需一个字体的轮廓数据量,而不是一个视频大小的图集。文本可以每帧变化,且零烘焙成本——这正是你想要的实时数据、用户输入和本地化内容。而且这一切都在一次绘制中完成,使用普通的片段着色器,不需要供应商扩展。
当你无法预测文本将如何被观看时,这很重要。图集、SDF 和 MSDF 都提前烘焙了固定分辨率,悄悄假定屏幕尺寸和观看角度的范围有界。当文本平面与相机之间的关系事先未知时——自由的 3D 相机、任意缩放、特写、或陡峭的离轴掠射角——这些烘焙近似就会失效:放大超过烘焙分辨率,图集会模糊,而 SDF 和 MSDF 会变圆并晕开;在斜角下,采样的场会出现锯齿。你无法为每种可能的视图预先分配足够的分辨率而不让存储爆炸。Slug 在变换之后逐像素解析地计算覆盖,因此无论观看者有多近、多远或多么倾斜,它都保持精确。细分是唯一共享这一特性的另一家族,且它以细分成本和更困难的抗锯齿为代价。
**看出区别** AlphaPixel 用他们的 `osgSlug` 集成(Slughorn)与单通道 SDF、MSDF、Rive 和 `osgText` 位图(加上其位图派生的 SDF)一起渲染了同一个大写字母 **R**。每个基于纹理的面板都获得了相同的预算:每 em 64 个纹素。

本文的“看出区别”部分首先描述了在烘焙尺寸下的正面比较,其中六个渲染器中有五个实际上完全相同,只有位图在放大时显示出其局限性。
*在烘焙尺寸下正面观看:*六个中五个实际上完全相同——Slughorn、Rive 和 MSDF 复现了轮廓;单通道 SDF 仅在字腿脚部的略微圆角处有所不同。异常值是位图,被放大约 4 倍,无法恢复它从未存储的细节。

本文解释说,在斜向 3D 透视下,基于图集的方法会模糊,SDF/MSDF 表现良好,但 Rive 仅限仿射的变换导致字形向边缘漂移,而 Slug 的解析方法保持精确。
*倾斜到掠射透视:*Slughorn 和三个距离场面板以干净的边缘将 **R** 保持在原位,因为它们在字形自身的平面内逐像素计算覆盖。位图在后退时模糊。Rive 的 **R** 形状不对——它的渲染器只接受 2D 仿射变换,所以透视投影会向边缘漂移。

本文描述了在极端放大下,解析曲线求值(Slug、Rive)保持完美锐利,而采样的距离场出现缺口伪影,位图则失去所有结构。
*在极端放大下聚焦到单条边缘:*只有基于曲线的渲染器(Slughorn 和 Rive)仍然产生笔直、干净的边缘。距离场面板在插值存储样本不再匹配真实曲线的地方出现缺口。位图已经消解成单一的灰色渐变。
**正面比较总结**
| 关注点 | 位图图集 | SDF | MSDF | 细分 / Rive | Slug | |---|---|---|---|---|---| | 任何尺度下都锐利 | 否 | 部分 | 大多 | 是 | 是 | | 锐利的角 | 烘焙尺寸时 | 否 | 是 | 是 | 是 | | 极小尺寸 | 按尺寸烘焙 | 弱 | 更好 | 好 | 好 | | CJK 内存占用 | 非常高 | 高 | 高 | 低 | 中等 | | 动态 / 变化的文本 | 重新烘焙 | 重新烘焙 | 重新烘焙 | 重新细分 | 零成本 | | 3D 和透视 | 柔软 | 可接受 | 可接受 | 好 | 极好 | | 重度动画矢量艺术 | 否 | 否 | 否 | 极好(Rive) | 好 | | 在低端 GPU 上运行 | 极好 | 极好 | 好 | 因设备而异 | 需要有能力的着色器 | | 实现复杂度 | 低 | 低 | 中等 | 高 | 中等–高 |
**那么该用哪一个?** 没有单一的赢家——每个工作都有合适的工具。
*在以下情况选用 Slug(通过 Slughorn 或其他实现):*你需要在任何尺度下以及在 3D 和透视(VR/AR/XR)下都清晰的文本,当你有巨大或动态的字形集时,当文本不断变化时,或者当你在 3D 场景中渲染混合矢量 UI 时。
*在以下情况选用 MSDF:*你需要清晰可缩放的 2D UI 文本,可以接受图集烘焙管线,并且希望在广泛的硬件上获得质量、性能和可移植性的当前最佳平衡。
*在以下情况选用 Rive 或其他细分渲染器:*你的主要工作负载是动画的、设计好的、会移动的矢量艺术——Rive 的管线正是为此而构建的。
*在以下情况坚持使用位图图集或 SDF:*你在受限或较旧的硬件上,你的文本尺寸和观看角度有界且事先已知,或者你需要尽可能简单的集成。
**关于提示的说明** 像 FreeType 这样的传统 CPU 渲染器会调整曲线以在特定尺寸下贴合像素网格(字节码提示)。Slug 的论点是它不需要逐尺寸的字形准备,因此其输出实际上是没有提示的。随着显示器密度增大,主要的渲染器已转向自动提示(忽略字节码并从轮廓推导提示)或完全不提示。像 Zig 移植版 *Snail* 这样的实现已经添加了 GPU 自动提示来复制这种行为,但这对 Slug 的采用者来说仍然是一个开放领域。
**着色器效果的权衡** SDF/MSDF 让你只需几行着色器代码就能添加轮廓、发光或柔化,因为距离值会告诉你“离边缘有多远”。Slug 的解析绕组测试只告诉你“在内还是在外”。在非常高分辨率的显示器和大尺寸下,这是一个优势——精确覆盖且没有距离场伪影——但你会失去廉价的基于距离的效果,必须单独处理抗锯齿。
**Slughorn:一个生产级的 Slug 实现** AlphaPixel 的 `slughorn` 是一个实现公共领域 Slug 技术的现代 C++20 库。它不对图形环境(OpenGL、Vulkan、WebGL、WebGPU、DirectX、Metal)做任何假设,并专注于简化从多个后端——FreeType、NanoSVG、Skia、Cairo、Blend2D——以及原生类 Canvas API 输入矢量数据。它支持 COLRv0/COLv1 emoji 合成、PBR/IBL 材质、渐变、遮罩、HUD 元素,以及 2D 正交和 3D 透视投影。像 `osgSlug` 这样的伴随项目协助 OpenSceneGraph/VulkanSceneGraph 集成。