新的方式,其實一點也不新,其實是一種稱為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描述文件
加油吧,騷年。