Unity在2018版本加入了TextMeshPro,并且现在已经将Text标记为Legacy。下面是我有关于TMP和Text的理解和思考。

Text的渲染原理

Unity的text组件是使用FreeType库来加载TrueType字体来实现的。通过加载TrueType字体并为每一个字形生成位图以及计算几个度量值(Metric),生成出一个具体的字形的位图纹理。
OpenGL_Text
text动态渲染一个字符过程为:判断一个字符是否已经存在字体纹理中,在则直接获取对应的纹理坐标渲染;若不在则读取ttf文件中字符数据生成字符的位图,并放入纹理中管理。若放入时图集已满,则会对图集进行一次扩大。
因为每个size、粗体、斜体的度量值不同,所以当修改Text的这些值的时候,纹理中会出现同一个字符多个位图的情况。
Text_Texture
Text_Texture

TMP的渲染原理

TMP是基于SDF(有向距离场)的技术渲染的,将字体中每个像素或顶点离其最近的形状边缘的距离值,存储到一张距离纹理中。所以TMP在显示前必须生成这个距离纹理,否则将无法显示(Unity中TMP会缺字出现口的原因)。因为显示的像素为动态计算该使用何种颜色和透明度,从而创建出精细、高品质的字体效果。所以TMP几乎不受放大的影响,能一直提供锐利清晰的图像。并且因为有距离信息,所以TMP能以一个较小的开销支持描边、投影等效果。
TextVsTmp
在Unity的SDF中可以看到这张纹理,可以看到字体内部为白色,外部为黑色,在字体边缘之间黑白渐变,也就是距离从近到远的变化。
TMP_SDF

TMP和Text的性能对比

这里使用的TTF文件为AlibabaPuHuiTi-3-65-Medium,TMP和Text渲染内容一篇滕王阁序。为了模拟实际项目中显示所有字符的需求,所有TMP和Text都选择字符模式都为Dynamic。下面从渲染DrawCall和内存两方面对比

Text的性能表现

首先是Text的DrawCall,得益于所有的字符都在一张字符纹理中,所以Text只有一个DC。
Text_DrwaCall
其次是内存因为滕王阁序的字符有限,所以Text的内存占用也仅有0.5MB。
Text_Mem

TMP的性能表现

TMP因为我是把纹理大小选择为了1024*1024,所以Unity把文章字符分到了3个不同的字符纹理中,最终使用了3个DrwaCall。
TMP_DrwaCall
内存占用也是3个字符纹理的占用,为6MB。
TMP_Mem

性能总结

在有限字符的渲染中,Text凭借只会取需要的字符位图存储,所以内存和DrwaCall下更优。但是这里TMP有着明显的优化空间,就是使用静态把所有的文字都的烘培一张纹理中。此时drwaCall为1,内存占用也为一张纹理2MB。
TMP_Better
但在实际的项目中,除非是没有聊天、排行榜等玩家自定义字符的功能,否则静态纹理一定会出现字符缺失的情况。对于Text来说,只能是使用Dynamic去扩容纹理大小,在纹理达到4096*4096后,也会出现多张纹理导致DrawCall上升。对于TMP因为官方支持了FallBack字体,所以可以使用一个静态纹理打入常用的3000或者7000字符,使用的FallBack字符再设置为动态,可以有效的避免游戏过程中因为贴图扩大的性能开销。

技术选型的思考

1、Text不支持图文混排,除非自己实现一套富文本图片解析。所以有图文混排需求建议无脑TMP。
2、仅有描边、投影的需求。之前实现了一个Text的描边和弯曲组件,所以在字符有限的项目中(不考虑本地化、玩家自定义情况)使用Text和TMP,在性能和效果上和TMP差不多。但在字符无限的项目中,推荐使用静态常用字TMP+动态TMP配合,既有较好的性能效果,也能使用TMP的各种功能。
3、对渲染效果更高的要求,这个就不用纠结了,美术一定会让你选TMP。

参考资料

[1].Unity字体渲染原理
[2].unity-UGUI源码分析(4):Text与TextMeshPro
[3].文本渲染-LearnOpenGL