文字渲染
全库目录221
本文目录12
字符串里的码点不总对应一张字形。连字、组合符号和从右到左文本先经过塑形,产生字形ID、前进量与偏移,渲染器再查询位图或距离场。排版与采样分开,才能定位缺字和边缘问题。
字符串与字形位置
字符、码点和字形
- 字符是读者理解的文字单位。
- Unicode 码点是字符编码空间中的编号。
- 字形(Glyph)是字体中实际绘制的形状。
- 字形 ID 是某个字体文件内部的索引。
一个码点可能对应多个字形,一个字形也可能由多个码点共同产生。连字、阿拉伯文连接形式、组合附加符号、Emoji 序列都需要文本塑形(Text Shaping)。
可靠流程是:
Unicode Text
→ 双向与脚本分析
→ 字体回退
→ Text Shaping
→ Glyph ID、Advance 和 Offset
→ 换行与对齐
→ Glyph Quad / PathFreeType 负责读取字体和栅格化字形。HarfBuzz 等塑形库根据脚本规则、Kerning 和字体特性生成字形序列。只按码点查询 FreeType 可以完成简单拉丁文本,无法覆盖完整国际化排版。
字形度量
每个字形常见度量包括:
- Advance:绘制后光标前进距离;
- Bearing:字形位图相对基线和光标的偏移;
- Width/Height:字形图像范围;
- Ascender/Descender:基线上下的字体范围;
- Line Gap:推荐行间距。
字形 Quad 的左上角不能只使用当前光标位置。它需要结合边距与基线。连续字形的位置由塑形结果中的前进量与偏移累积,不应自己再重复应用一遍 Kerning。
塑形位置与位图矩形
塑形器输出的位置必须先转换为同一像素尺度。以下约定布局屏幕坐标向右、向下为正,塑形器的y偏移和字体边距向上为正;基线光标为(penX, penY)。位图左上角为:
left = penX + xOffset + bitmapLeft
top = penY - yOffset - bitmapTop
right = left + bitmapWidth
bottom = top + bitmapHeight
penX += xAdvance
penY -= yAdvance若HarfBuzz字体缩放配置产生26.6定点数,先除以64再参与像素计算;若配置为字体单位,则按字号与units-per-em转换。不能仅凭库名假定输出单位。FreeType的轮廓度量与栅格化位图边距也要区分,最终画位图时使用与这张位图匹配的数据。
例如基线在(10,40),塑形偏移为(2,3),位图边距为(1,8),尺寸为6×9,矩形应为(13,29)到(19,38)。组合重音符号可以前进量为零,却仍有独立位图和偏移;把位图宽度当作前进量会把组合符号挤成两个字符。右到左段落按塑形后的顺序与位移排布,不能再把字形数组机械反转。
字体回退也要保持塑形上下文。遇到缺字时,按脚本段、字素簇和字体覆盖重新塑形适当范围;逐码点临时换字体可能破坏连字与组合。光标、选区和删除操作保留字形到原文本簇的映射,渲染四边形的位置不能直接充当文本索引。
字形的采样表示
位图字体与字形图集
最直接的方法是把字形栅格化成 透明度遮罩,再把许多字形放进 字形图集。每个字形记录图集UV、像素尺寸、Bearing 和 Advance。
图集能让多个字形共享纹理绑定并进入同一批次,但要处理:
- 字形边缘 Padding,避免双线性过滤串色;
- 动态加入新字形时的空间分配;
- 图集满后的淘汰、扩容或分页;
- 不同字号、Hinting 和语言字符集;
- 图集更新期间的资源同步;
- Mipmap 对小字边缘的影响。
CJK 字符集很大,通常按实际使用字形动态缓存,或按语言资源预烘焙多个 图集页。运行时缺字需要显示明确的替代字形,并记录缺失字体和码点。
SDF 和 MSDF
有符号距离场(Signed Distance Field,SDF)保存采样点到字形边界的带符号距离。下面的示例把零距离映射到 0.5,字形内部映射到更大的值。Shader 在这条等值线附近恢复覆盖率,因此同一张 Atlas 可以覆盖更大的缩放范围。
float GlyphAlpha(float2 uv)
{
float distance = GlyphAtlas.Sample(LinearClamp, uv).r;
float smoothing = max(fwidth(distance) * SdfSoftness, 1e-4);
return smoothstep(0.5 - smoothing,
0.5 + smoothing,
distance);
}fwidth 根据屏幕导数调整过渡宽度,使缩放时边缘保持接近一个像素。若生成器使用相反的距离符号,smoothstep 的方向也要反转。SDF 分辨率、Distance Range 和生成时的归一化方式必须与 Shader 中的阈值一致。
普通单通道 SDF 在尖角处容易变圆。MSDF 把不同边的距离编码到 RGB,通过中值恢复距离,能保留更多尖锐转角。例如采样RGB为(0.2,0.7,0.6),中值为0.6;可用max(min(r,g),min(max(r,g),b))得到该值,再按同一符号和距离范围恢复覆盖。只读红通道会把这个样本判到轮廓外,而中值位于轮廓内,说明RGB保存的不是三种文字颜色。生成时的边着色决定每条边写到哪些通道,错误分组会破坏中值在转角处的重建,运行时扩大平滑宽度只能模糊错误。它仍需要正确的边着色、Padding和采样范围。
极小字号更依赖 Hinting 和像素对齐,位图字形可能比 SDF 清晰。超大标题、描边和发光适合 SDF/MSDF。一个项目可以按字号和平台组合使用。
颜色和混合
字形图集 通常提供 Coverage,文字颜色来自顶点或常量。使用预乘 Alpha 时:
Blend State 使用 One, OneMinusSrcAlpha。图集覆盖率 属于线性覆盖数据,采样时不做 sRGB 解码;文字颜色则需要遵守 UI 颜色空间和最终显示变换。
LCD Subpixel Rendering 依赖显示器子像素排列和最终合成位置。旋转、缩放、HDR 合成和不同面板都会破坏假设,因此游戏 UI 常使用灰度抗锯齿。
验证方法
测试CJK、阿拉伯文、组合符号和Emoji,显示基线、前进量与字形边界。图集更新与布局失效同时检查,位图与SDF在小字号、旋转和缩放下分别对照,覆盖纹理不做sRGB解码。
编译记录:2026-10-22运行verify.py,从本页片段补最小入口,以DXC的ps_6_0编译GlyphPS通过。结果记录保存工具版本;命令为python 知识库/examples/revision_22_26/verify.py。编译检查不包含完整引擎或GPU输出。
参考实验与证据
CPU参考模型的glyph_quad检查偏移、bearing与屏幕y方向,包括零前进量组合符号的位置。2026-10-22执行python 知识库/examples/revision_22_26/test_reference.py,21项通过,其中2项属于字形矩形。这不是HarfBuzz国际化测试;真实字体塑形、回退、Shader像素结果与GPU时间尚未验证。
相关主题
参考资料
- FreeType Glyph Conventions: Glyph Metrics,Baseline, pens and layouts与Typographic metrics and bounding boxes;2026-10-22核读,支持基线、bearing和advance的区分。
- HarfBuzz Shaping and shape plans,Shaping and buffer output;2026-10-22核读,支持字形替换和定位由塑形器处理。
- Viktor Chlumský,msdfgen,Using a multi-channel distance field一节;2026-10-22核读,支持RGB距离中值与距离范围的还原约定。