OpenGL字体渲染与纹理打包:纹理空间不足问题该如何处理?
下面是工业界常用的几种处理思路,可根据业务场景组合使用:
动态LRU纹理图集
不用提前打包所有字符到图集,运行时按需把用到的字符渲染到当前的纹理图集里,搭配LRU缓存策略管理字符的生命周期。单张图集满了之后,直接淘汰最久没被使用的字符,腾出空间给新字符。
大部分场景下常用的ASCII、常用汉字等高频字符会长期驻留在图集里,只有极少量生僻字、特殊符号会触发缓存淘汰,几乎不会出现频繁切换纹理的情况。
实现时注意给纹理设置GL_CLAMP_TO_EDGE的环绕参数,避免采样时出现邻接字符的边缘污染。按图集分组批量绘制
如果业务确实需要同时渲染的字符量远高于单张纹理的容量上限,可以提前把字符按Unicode区块、使用频率拆分到多张固定的纹理图集里。渲染前先把整段字符串的字符按所属图集分组,同组的所有字符合并为一次绘制调用,不同组之间再切换纹理。
比如把ASCII字符存在图集0、常用3500汉字存在图集1、生僻字和特殊符号存在图集2,一段混合内容最多也只需要3次绘制调用,性能开销远低于逐字符切换纹理的方案。2D数组纹理规避切换开销
OpenGL 3.0及以上版本支持GL_TEXTURE_2D_ARRAY特性,可以把多张同尺寸、同格式的字符图集存在同一个纹理对象中,不需要切换纹理绑定。只要在顶点属性里新增一个图层索引字段,就可以直接在片元着色器里采样对应图层的字符,整段字符串只需要一次绘制调用就能完成,完全消除纹理切换的开销。SDF字体压缩容量
改用有向距离场(SDF)格式存储字符,单张纹理能容纳的字符数量是普通位图字体的3~5倍,大幅降低需要多图集的概率。同时SDF支持无损缩放,不需要为不同字号单独存储字符,进一步降低了纹理空间的占用。
实测显示,除非是需要同时渲染全量Unicode字符的极端场景,动态LRU图集+按图集分组的组合方案就能覆盖99%的业务需求,额外性能损耗可以控制在5%以内。
内容的提问来源于stack exchange,提问作者rafoo

