字体勾边渲染的简单方法
我们的游戏中需要对渲染字体做勾边处理,有种简单的方法是将字体多画几遍,向各个方向偏移一两个像素用黑色各画一遍,然后再用需要的颜色画一遍覆盖上去。这个方法的缺点是一个字就要画多次,影响渲染效率。
前几年有人发明了另一种方法,google 一下 Signed Distance Field Font Rendering 就可以找到大量的资料。大体原理是把字体数据预处理一遍,把每个像素离笔画的距离用灰度的形式记录在贴图上,然后写一个专门的 shader 来渲染字体。好处是字体可以缩放而不产生锯齿,也比较容易缺点边界做勾边处理。缺点是字模数据需要离线预处理。
我们的手游项目以及 3d 端游项目都大量使用了勾边字体,我希望直接利用系统字体而不用离线预处理字体把字体文件打包到客户端中。前段时间还专门实现了一个动态字体的贴图管理模块 。btw, 苹果的平台提供了高层 API 可以直接生成带勾边效果的字模。
但是,勾过边的字模信息中同时包含了轮廓信息和字模主体信息,看起来似乎很难用单通道记录整个字模数据了。这给染色也带来了麻烦。
见这张图,是一张带勾边信息的字模。轮廓是黑色的,字体是白色的。从颜色通道上看,有黑白灰的过渡。但灰色部分 alpha 通道对应量却不相等。轮廓向字体主干过渡的地方,色彩是灰色的,但是 alpha 值为 1.0 。也就是说,alpha 通道是独立的。
我们需要两个通道,颜色通道和 alpha 通道,来保存完整的字模信息才能在最后正确的渲染到屏幕上。很多显卡硬件并不支持两通道贴图,所以要么我们用一个 RGBA 四通道贴图来保存,要么用两张单通道贴图。
我想了个简单的方法只用一个通道就可以保存全部信息,那就是把 alpha 为 1.0 的像素的灰度影射到 0.5 到 1 的区间;把 alpha < 1.0 的部分像素的 alpha 值影射到 0 到 0.5 的区间。这样做可行是因为,经过勾黑边的白字,其 alpha 小于 1.0 的像素一定都是黑色的,也就是 RGBA 都相等。所以信息只损失了一个 bit 就保存了下来。
经过处理后的贴图是这样的。可以看到过渡是均匀的,所以并不会影响纹理采样。最终我们写一个简单的 shader 就可以对字体做染色了。最终效果是这样的:
Shader 也非常简单,只需要取出单通道贴图上的灰度值 G ,
Alpha := clamp(G * 2.0 , 0, 1.0)
Color := clamp((G-0.5) * 2.0, 0, 1.0)
就可以还原出原来的颜色通道和 alpha 通道的信息。
Comments
Posted by: BearOcean | (7) February 24, 2015 12:36 PM
Posted by: 如何管理好团队? | (6) October 23, 2013 11:28 AM
Posted by: 业务云笔记 | (5) October 16, 2013 11:46 AM
Posted by: 好笔头业务云笔记 | (4) October 16, 2013 10:55 AM
Posted by: 孤单 | (3) September 12, 2013 01:09 PM
Posted by: Allan | (2) September 10, 2013 11:49 AM
Posted by: 高强 | (1) September 4, 2013 10:41 PM