新的方式,其实一点也不新,其实是一种称为signd distance field的矢量图保存和渲染方法。
早在二零零几年,就已经有一些游戏应用了这种技术,而他用于字体渲染也被发掘多时。
但是这个世界,技术成果转化的速度总是很慢很慢,这个优势明显的技术一直没有普及开来。
1.矢量的优势
我们先来说说矢量和位图的区别:
一个汉字,人眼看起来就是一个形状
这是位图,是上面那个汉子用位图存储方式放大后的一个局部
再来看看矢量化放大的效果
瞎子也能明白,矢量是适合缩放的。
现在游戏中,因为大量使用位图字体,而位图字体并不适合缩放,于是如果要用不同大小
位图放大后都是这个屌样子。
2.Signeddistancefield表示法
这也是为什么我们要引入signddistancefield,他是一种矢量表示法。
用位图,表示矢量,表示方法我随便一说,你随便一听,并不是太有所谓。因为我会给你一套东西,你能照猫画虎用起来。
还是这张图,把他存成一个很小的像素图,只不过每个像素存的不是颜色,而是离边的最短距离。
现在白色部分,在形状内,存为负值,黑色部分,在形状外,存为正值。
请忽略我拙略的绘图技巧。
这样保存后的图片,你可能会问我,一个像素分量是0~255,咋存正负,我会告诉你,128当零,行不行。
这个细节不深究,让我们换个方法来表示,本质上是为了区分边的内外。比如我设定颜色255在形状内最深的地方,127是形状边,0是离形状最远的地方,当然我们也不需要离边太远的数据,可以让离边超出一定距离的数据全是255和零,只有在边附近,有过度。
这是一个文字用signeddistancefield表示法保存后的结果。
如我们所言,255是离边最远,在形状内的部分,外边的纯透明部分,是alpha为零里边最远的部分。
这样的表示法,在还原成形状的时候,我们只要判断 >就行了,>127的部分画出来,就是原来的形状
>200的部分画出来,是个向内收缩的形状。
>100的部分画出来,是个向外扩张的形状。
我们如果要做描边,可以在shader里写个if
比如>127纯白 100-127 黑色。
你可能会发现一个问题,这种情况只有透明不透明,两种情况。
这个问题可以解决,往下看
而且因为源数据是离边的距离,两个像素的插值就是当前像素离边的距离。
这个信息是线性的,插值数据不会偏离太远,抗缩放性能非常好。
3.实战SignedDistanceField
第一步,把图片或者字体保存为signeddistancefield
因为SDF实际上已经是应用很多的技术,所以工具并不缺乏。
我就找到了两个。
http://www.gamedev.net/topic/491938-signed-distance-bitmap-font-tool/
我们简称他BFTool,我很喜欢这个工具,因为很小巧,几百k,还包括源码和测试字体和图片。
还有龚敏敏的KLAYGE里面也提供了一个字体保存为signeddistancefield的工具
不过这玩意从他的一堆c++代码里剥出来要花点功夫。
我们这里就介绍一下BFTool
先下载这个工具
http://www.lonesock.net/files/SDFont.zip
他没有图形界面,but,whocare
找到这个文件
可以把一张png 或者一个ttf拖上去
然后输入一些参数,就会生成啦。
先找张图片
比如这个,至少1024以上
让我们把他输出成32*32的sdf图像
你可能无法期待这32*32个像素可以输出什么像样的效果。
来吧,见证奇迹的时刻到了。
这就是32*32的SDF图像还原出的效果,还顺便做了描边
更粗的描边
向内描边
使用的shader关键代码如下
http://code.taobao.org/p/fightbeta/src/trunk/dfbitmap/
很简单,就是ifif,描边没有任何开销,不过去狗牙要多几次采样。(这地方是我考虑不周到)
后来考虑了一下,不用多次采样,将距离多分几段插插值,不用额外采样就能完美去狗牙
注,这张图是展示去狗牙效果,是128分辨率的,和上面的不一致。
能明白我再说什么的人自然明白。
4.关于字体
你已经看到了32*32的SDF图像能做到什么,如果你把文字生成为32*32的足够精细了。
一张2048的贴图,可以放4000个32*32的文字。
而且抗缩放,大小字号一图搞定。
只要把一个ttf字库拖入sdfont.exe
给他参数,你就能得到一张sdf图片,和一张字符uv描述文件
加油吧,骚年。