Hotdry.
web

距离场字体纹理生成与栅格化:阈值渲染、Mipmapping及各向异性过滤

从矢量轮廓生成SDF字形纹理,实现阈值栅格化、多级渐进纹理与各向异性过滤,支持游戏与Web的可缩放抗锯齿字体渲染。

在游戏和 Web 渲染中,实现高质量、可缩放的抗锯齿字体是常见挑战。传统位图字体图集在缩放、旋转或透视变换下容易产生锯齿或模糊,而签名距离场(Signed Distance Field, SDF)字体通过存储每个像素到最近轮廓的签名距离,提供了一种高效解决方案。本文聚焦单一技术点:从矢量轮廓生成 SDF 字形纹理,并讨论阈值栅格化、Mipmapping 及各向异性过滤的工程实践,帮助开发者落地可缩放字体渲染。

SDF 字形纹理生成原理与流程

SDF 纹理的核心是计算每个像素到矢量轮廓(outline)的签名距离:轮廓内为负值、外为正值、边缘为零。这种表示允许简单片元着色器通过阈值判断生成平滑边缘,支持任意缩放而无需重新栅格化。

生成流程从 TTF/OTF 字体或 SVG 路径开始:

  1. 解析字体,提取单个字形的 Bezier 轮廓。
  2. 栅格化为固定分辨率画布(如 32x32 或 64x64 像素)。
  3. 对每个像素计算到最近轮廓边缘的欧氏距离,并归一化为 [0,1] 范围(0.5 为中心)。
  4. 为提升角落锐利度,使用多通道 SDF(MSDF):RGB 通道分别存储到最近三条不同 “颜色” 边缘的距离,中位数解码得真距离。

推荐工具 msdfgen(C++ 库或 CLI):

msdfgen msdf -font font.ttf 'A' -o glyph.png -dimensions 48 48 -pxrange 6 -autoframe
  • -pxrange 6:距离场范围 6 像素,平衡锐利与内存(4-8 典型)。
  • 分辨率 48x48:适合 UI 字体,复杂字形用 64x64。 证据显示,MSDF 在低分辨率下角落误差降低数个数量级,比单通道 SDF 锐利。

构建图集时,用 msdf-atlas-gen:

msdf-atlas-gen -font font.ttf -charset "ASCII" -type msdf -pxrange 4 -size 48 -texture 1024 1024 atlas.png atlas.json
  • 纹理尺寸 1024x1024,支持数百字形。
  • 填充 2-4 像素防渗色。 参数清单: | 参数 | 推荐值 | 说明 | |------|--------|------| | pxrange | 4-8 | 边缘像素范围,小值锐利大值平滑 | | glyph size | 32-64 | 平衡质量与图集大小 | | padding | 2px | 防线性插值渗色 | | format | PNG/RGBA | 线性空间,非 sRGB |

阈值栅格化渲染

渲染依赖片元着色器阈值化距离:

float median(float r, float g, float b) { return max(min(r, g), min(max(r, g), b)); }
float sd = median(texture(msdf, uv).rgb);
float pxDist = screenPxRange(msdf) * (sd - 0.5);
float alpha = clamp(pxDist + 0.5, 0.0, 1.0);
  • median解码 MSDF。
  • screenPxRange()计算屏幕像素距离范围:
float screenPxRange(sampler2D tex) {
    vec2 unitRange = pxrange / vec2(textureSize(tex, 0));
    vec2 screenTexSize = vec2(1.0) / fwidth(uv);
    return max(0.5 * dot(unitRange, screenTexSize), 1.0);
}

使用fwidth自适应字体大小 / 距离,确保抗锯齿宽度正确(1-2 像素)。

为更平滑,可替换clampsmoothstep(-0.5, 0.5, pxDist),模拟超采样。Gamma 校正可选:alpha = pow(alpha, 1.0/2.2),但依赖渲染管线线性空间。

WebGL 示例(twgl.js 简化):

  • 顶点:四边形 quad,传递 uv、字体大小 uniform。
  • 状态:GL.LINEAR 过滤,GL.CLAMP_TO_EDGE 包裹,无 Mipmap。 结果:在任意缩放下边缘保持锐利,支持 outline(多阈值层)/ 阴影(偏移采样)。

Mipmapping 处理

标准 Mipmap 对 SDF 失效:下采样平均距离值破坏距离场性质,导致小尺寸文字变粗 / 细或溶解。

解决方案:

  1. 禁用 Mipmap(首选 UI / 中距离):gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.LINEAR);
  2. 特殊 Mipmap:逐级重新计算 SDF(离线工具),或 shader 动态阈值补偿(复杂)。
  3. LOD 切换:远距离 fallback 位图图集。

实践:游戏 UI 禁用 Mipmap 节省内存,Web canvas 类似。证据:Unity TextMeshPro 默认无 Mipmap,依赖 SDF 自缩放。

各向异性过滤优化

Anisotropic 过滤(AF x4-x16)增强斜视锐利,但对 SDF 有害:多方向采样平均异质距离,扭曲边缘。

最佳实践:

  • 禁用 AFGL_TEXTURE_MAX_ANISOTROPY_EXT = 1.0
  • 保留 bilinear(GL.LINEAR),依赖 SDF 抗锯齿。
  • 极端情况(如 3D 标签斜角):shader 用 derivatives 调整阈值宽,或 MSDF + 高 pxrange。

参数:OpenGL glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0);

可落地工程清单

  1. 预处理

    • 选常用字符集(ASCII + 常用汉字)。
    • 生成 MSDF 图集,pxrange=6,padding=3。
    • JSON 存 metrics(advance, bearing)。
  2. 运行时

    • 纹理:LINEAR 过滤,无 Mip/AF,CLAMP 包裹。
    • Shader:MSDF median + fwidth pxRange + clamp/smoothstep。
    • Uniform:pxrange, fontScale。
  3. 监控 / 回滚

    • 阈值:alpha<0.01 视为透明,防鬼影。
    • 性能:图集 < 2MB,shader<10 ALU。
    • 回滚:小尺寸 < 12px 用位图。
  4. 平台调优

    平台 pxrange Mip AF
    WebGL 6 Off Off
    Unity 4-8 Off x2 max
    Games 8 Special Off

风险:三边缘汇合处(如 “k”)MSDF 微 artifact,pxrange>8 缓解;内存:单图集~1MB / 字体。

此方案在 WebGL demo 中证明:缩放 30x 仍锐利,优于位图。

资料来源

  • Red Blob Games SDF 笔记。
  • msdfgen GitHub 文档。
  • Unity TextMeshPro SDF 指南。

(字数:1256)

查看归档