• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            OpenGL點(diǎn)陣字體繪制終極解決方案!

            OpenGL點(diǎn)陣字體繪制終極解決方案!哈!

            tsuui posted @ 7 years ago in Coding with tags OpenGL 中文顯示 freetype , 12160 readers

            事情總在變化, opengl迎來(lái)了3.3以及4.1的進(jìn)化, 相信今后的擴(kuò)充也會(huì)朝著這個(gè)方向. 對(duì)于字體渲染方面, 也并不是什么壞事. 今后有時(shí)間再寫篇關(guān)于3.3和4.1的全屏字體渲染的新方案, 仍然是結(jié)合freetype2的, 相信隨著freetype2的進(jìn)步, 和對(duì)它的逐步認(rèn)識(shí), 應(yīng)該會(huì)比現(xiàn)有方案更簡(jiǎn)單高效... 現(xiàn)在最最最重要的事是...睡覺!!!

            對(duì)于此文, 大家僅做參考吧.


            經(jīng)過多次修改測(cè)試,字體問題終于有了個(gè)比較完美的解決方法了,貼出來(lái)亮亮~~

            此法可以說完全是“紅寶書”(即《OpenGL編程指南》)所賜, 此篇也不過是一些實(shí)踐心得和我自己對(duì)字體顯示方法的一些體會(huì)罷了。

            下面就來(lái)介紹這個(gè)所謂的“終極解決方案”,對(duì)于待解決的各種問題,都有著多種可供選擇的方案,就讓我來(lái)邊比較邊描述吧:

            1. 渲染方式和幀數(shù)

            不管是不是OpenGL平臺(tái), 在每個(gè)3D平臺(tái)中,  點(diǎn)陣字體無(wú)非兩個(gè)用處: 要么做效果,要么做提示。效果就是標(biāo)題文字、按鈕之類的,我們一般稱之為banner,titile,caption的東西; 提示就是指一些有動(dòng)態(tài)更新要求的文字,如控制信息提示,  調(diào)試模式下的對(duì)象名稱、坐標(biāo)等, 還有就是交互場(chǎng)合,比如聊天。

            兩種應(yīng)用需求有所不同,但不管是哪種,在OpenGL中我能找到的直接支持字體的,只有三種方法,選擇他們的標(biāo)準(zhǔn)只有一個(gè)——速度:

            ● glBindTexture, 紋理貼圖,連文字帶背景做好一張大圖,  按需地選取各個(gè)文字子圖像,再貼到相應(yīng)位置的矩形上。貼圖能夠?qū)崿F(xiàn)的文字效果最多,你可以把文字紋理映射到空間任意位置的巨型上,可以隨意的旋轉(zhuǎn)縮放和變形。在不要求大量動(dòng)態(tài)更新文字內(nèi)容的地方,可以選用此方法。大部分的小型3D游戲,都采用了這樣的方式顯示文字,速度夠快,能實(shí)現(xiàn)所有的變換效果。

            不足之處是:

            很難實(shí)現(xiàn)多顏色混合顯示的文字,因?yàn)闉榧y理設(shè)置顏色需要的步驟十分繁瑣,需要反復(fù)切換和設(shè)置紋理函數(shù)和像素傳輸轉(zhuǎn)換函數(shù),難免影響性能;

            文字內(nèi)容不能靈活的更換, 除非你打算用很多碎小的紋理來(lái)拼湊文章;但隨著碎小圖片的增多,頂點(diǎn)的和紋理對(duì)象也大量增加,需要大量額外的片段處理和過濾操作,會(huì)明顯拖慢處理流水線,在要求顯示大量動(dòng)態(tài)文本的場(chǎng)合下力不從心。不過好在OpenGL在處理紋理對(duì)象時(shí)多數(shù)情況是使用硬件實(shí)現(xiàn)的,速度不會(huì)慢太多,但也絕對(duì)不夠塊(你可能玩過這樣的3D游戲:圖像效果場(chǎng)景規(guī)模都一般,可鼠標(biāo)速度慢得難以忍受,出現(xiàn)這種情況,九成的原因是頂點(diǎn)片元過多造成的,單次場(chǎng)景同時(shí)顯示的紋理片段過碎過多,都會(huì)成倍地同時(shí)增加頂點(diǎn)和像素片元,拖慢速度,鼠標(biāo)有時(shí)間響應(yīng),卻沒時(shí)間畫出來(lái));

            還有就是變換拉伸后,紋理字體會(huì)出現(xiàn)模糊的現(xiàn)象,有些人建議打開Anisotropic Filtering(各向異性過濾)開關(guān), 利用反走樣解決,但效果似乎也不穩(wěn)定,在轉(zhuǎn)角過大、近距離或光線角度太偏的情況下,效果就越來(lái)越差了,我想這是紋理映射的通病吧,不可能就一張圖你從哪里看都一樣的清晰啊,也有人用多等級(jí)的紋理和Mipmap解決,本人沒試驗(yàn)過(比較麻煩)所以沒什么發(fā)現(xiàn)權(quán)。

            ● glDrawPixels,像素繪制,任何紋理能夠支持的圖像格式,它都能支持,縮放也很簡(jiǎn)單,也可通過設(shè)置像素傳輸和像素封裝函數(shù)實(shí)現(xiàn)一些其他的效果。

            缺點(diǎn)是:

            他同紋理一樣,很難靈活設(shè)置顏色; 

            只能在光柵上繪制,若需要各種變換效果,還要開辟額外的輔助緩沖和紋理對(duì)象;

            而最大最大的問題就是速度! 像素在顯示之前的處理動(dòng)作是沒有經(jīng)過加速的,也就是說不管你有沒有把他編譯到顯示列表,像素的轉(zhuǎn)換傳輸?shù)葎?dòng)作每次都照做不誤,它不同于紋理對(duì)象中的像素,多數(shù)OpenGL實(shí)現(xiàn)沒有對(duì)它開辟專屬的顯存區(qū)域(這種說法有待考證,但實(shí)際測(cè)試中效率確實(shí)很差,編程指南中有特定篇幅介紹了如何提高像素繪制的效率,但即使?fàn)奚磺匈Y源來(lái)保證效率,實(shí)測(cè)效果仍然很難讓人滿意)。

            所以,雖然 glDrawPixels似乎是三種方法中最簡(jiǎn)單有效的, 可實(shí)際運(yùn)行起來(lái)卻是三種方法中最慢的!所以如果你要繪制大量點(diǎn)陣字,又想保證幀數(shù)的話,寧愿去考慮紋理貼圖,也不要在這個(gè)函數(shù)上花太多心思。

            ● glBitmap,位圖,如果你想在你的3D引擎里添加一個(gè)控制臺(tái),這個(gè)是唯一的選擇,96個(gè)可打印字符做成位圖映射到索引為0x20~0x7F的顯示列表,供隨時(shí)調(diào)用。就算直接用glBitmap也來(lái)的及,對(duì)幀數(shù)的影響也不算大,  三種方法中它的速度最能讓人滿意, 且能通過設(shè)置光柵顏色靈活改變位圖字體的顏色。想象一下,如果你的控制臺(tái)里的warning error 普通的log message和user command分別使用了不同的顏色顯示,而為實(shí)現(xiàn)這個(gè)既酷又實(shí)用的效果,所付出的代價(jià)僅僅是在設(shè)置光柵前加個(gè)glColor這么簡(jiǎn)單而已。

            缺點(diǎn):

            只能在光柵上繪制,若要縮放旋轉(zhuǎn)之類的變換,需要額外的處理工序,但由于其本身的速度優(yōu)勢(shì),這些工序一般不會(huì)對(duì)幀數(shù)有太大的影響;

            另外由于位圖只有黑白單色,無(wú)法表示灰度,鋸齒問題嚴(yán)重,如果只顯示英文字體還好,一旦要顯示中文,文字效果很差,實(shí)在是褻瀆中華文化!當(dāng)然如果你知道怎么在OpenGL里實(shí)現(xiàn)一個(gè)和ClearType類似的技術(shù),那另當(dāng)別論。

             

            以往對(duì)于全屏字體渲染,glBitmap一直是我心中的痛,難以割舍它的高速,又無(wú)法忍受它的效果, 直到前一段在讀編程指南時(shí),無(wú)意間發(fā)現(xiàn)了一種利用glBitmap顯示反鋸齒字體的技巧。當(dāng)時(shí)反復(fù)讀了幾次,貌似明白了上面的意思,拿到機(jī)器上試了試, 果然天才, 很好地解決了鋸齒的問題,相見恨晚,感嘆讀書太不認(rèn)真,怎么早沒發(fā)現(xiàn)!!  下面簡(jiǎn)單描述一下這個(gè)方法:

            對(duì)于一副256灰度圖像,每個(gè)像素使用了一個(gè)字節(jié)表示0~255個(gè)灰度,而位圖只有一位0或1,乍一看不太可能,但位圖可以靈活設(shè)置顏色的特點(diǎn),成了突破口。既然位圖在設(shè)置光柵前可以使用glColor為光柵指定"當(dāng)前光柵顏色",不僅如此,我們還可以指定顏色的alpha值,從而繪制明暗相間的彩色位圖,了解了?

            把一個(gè)反鋸齒的灰度字體圖像分為多幅位圖,假設(shè)分為4張位圖,第一張:使灰度1~63的相應(yīng)點(diǎn)置1,其他點(diǎn)置0;第二張:64~127的置1,其他置0...以此類推, 灰階每上升64的點(diǎn)都集中到同一張位圖上。然后,打開混合,使用4次glBitmap調(diào)用繪制出來(lái),每次繪制前將光柵顏色設(shè)置成與圖像對(duì)應(yīng)階段的灰度,像下面這樣: 

            GLfloat curColor[4] = { r, g, b, a*0.25f}; //假設(shè)當(dāng)前顏色為 (r,g,b,a) for (int i=0; i<4; ++i) { glColor4fv(curColor); glRasterPosiv(curPos); glBitmap(w,h, 0,0, 0,0, bitmap[i]); //當(dāng)前alpha增幅0.25, 4次增至1.0 curColor[3] += a*0.25f; } 

            就相當(dāng)于讓一張256灰階的位圖降低到5灰階。這么做的效果如何呢?

            下圖是我在glut這種超慢框架下的測(cè)試的:

            中間的截圖是用glDrawPixels在打開freetype2的autohinting選項(xiàng)下渲染的256灰階字體, 上下兩張截圖都是使用glBitmap繪制的,沒有打開autohintng,上面的是3副位圖(4灰階)/字,下面的是4副位圖/字。glDrawPixels是使用了顯示列表繪制全屏1003個(gè)漢字的,已經(jīng)累成14FPS了,而glBitmap是沒用顯示列表的,同樣1003字一屏,在glut下也能達(dá)到50FPS以上! 近乎完美!

            (窗口分辨率是960x600)

             同時(shí),由于每個(gè)像素變成了4個(gè)bit表示(4張圖每張1bit),使存儲(chǔ)字模所需的空間降至原來(lái)的一半。

             

            1. 字庫(kù)和編碼映射

            除了glDrawPixels,每一種方法都有應(yīng)用它的理由,但不管你用哪一種,要克服的最大困難除了渲染速度,就是字庫(kù)問題了! 讀取字庫(kù)建議使用FreeType2這個(gè)開源目, 它支持當(dāng)今幾乎所有流行格式的字體文件,我們可以選擇它來(lái)作為字體導(dǎo)入的工具,當(dāng)然也可以把它link到你的程序中,實(shí)時(shí)的載入ttf字體并按需生成字模圖像。解決字庫(kù)的讀取問題,F(xiàn)reeType2絕對(duì)是上上之選,就這么簡(jiǎn)單~

            當(dāng)然, 如果你只想支持普通的96個(gè)可打印字符,除了glDrawPixels,其他兩種方式隨便用——想要效果就用glBindTexture、想要簡(jiǎn)單方便就glBitmap,然后關(guān)掉瀏覽器、合上參考書,最多半個(gè)小時(shí)你的字體問題就有著落了! 可如果你想要支持中文??龐大的字庫(kù)體積是你不得不考慮的另一個(gè)問題, 何為龐大?讓我們簡(jiǎn)單地算下:

            GB2312編碼包含7445個(gè)字符,其中漢字6000多個(gè),GBK編碼下僅漢字就有20902個(gè),最新國(guó)家標(biāo)準(zhǔn)GB18030-2005,總共76546個(gè)字符, 而目前的Unicode字符集,已經(jīng)增至超過10萬(wàn)個(gè)字符,雖然現(xiàn)在還沒有哪個(gè)unicode字庫(kù)能支持到這么多字符(難道真的有?),但至少20000個(gè)還是有的! 而這些字符都是分散在編碼空間中的,就是說編碼是不連續(xù)的,不能使用連續(xù)的顯示列表索引作簡(jiǎn)單的映射(即使連續(xù),這么龐大的數(shù)目,就算顯示列表沒有上限,它所占據(jù)的顯存空間也相當(dāng)可觀),因此不得不為‘字符編碼’到‘字模索引/列表索引’建立查找表。

            最猛的做法是,在內(nèi)存平鋪整張表,字模全部存入內(nèi)存,一步索引到字模,生成顯示列表,下次再繪制字模時(shí)只需索引到顯示列表而不必去取字模。這樣做好像也沒什么問題,沒什么問題?如果真的沒問題就不會(huì)是最猛的了——對(duì)于GB2312和GBK這種"小型"多字節(jié)編碼就需要盡1MB的空間,對(duì)于unicode最少最少需要近4MB的空間,而在這個(gè)大表里,八成以上的內(nèi)容是普通人這輩子都用不上的,而每刷新一幀,你的每個(gè)要顯示的字符都要重復(fù)查表一次,在這樣大的空間中頻繁查表,產(chǎn)生頁(yè)交換的可能非常的大,速度不慢才怪,絕對(duì)不比你每次調(diào)用freetype實(shí)時(shí)轉(zhuǎn)換灰階來(lái)的快,而且還很浪費(fèi)。

            我建議的方法是利用std::map!當(dāng)然如果你有自己的紅黑樹類和allocator也可以自己做一個(gè)map,效率上可能更勝一籌。map的作用是把字模信息映射到字符編碼,動(dòng)態(tài)的載入我們僅有可能用到的那幾千個(gè)字模信息,這樣既節(jié)省了空間(省點(diǎn)是點(diǎn)),又比較高效。另外,這里不必專門為map設(shè)定空間限制,map在到達(dá)一定大小后(大約7000個(gè)節(jié)點(diǎn))或每過一段時(shí)間后將查找表clear掉就可以了,除非你要在程序里顯示《說文解字》全篇,否則要讓map增大到5000節(jié)點(diǎn)都是個(gè)相當(dāng)有難度的工作。

             

            1. 定制自己的字體文件

            哎……這也是被逼無(wú)奈,如果你夢(mèng)想著自己的圖行引擎能有全功能的中文支持(顯示、輸入),你必須一再考慮速度的問題!因?yàn)橹形膶?shí)在是太多了……而且萬(wàn)把字符一會(huì)要查表一會(huì)要轉(zhuǎn)換圖像一會(huì)又要排布文字,各個(gè)環(huán)節(jié)都不像西文那樣方便直接, 都需要額外的繁瑣的計(jì)算!如果你還要些特效,你一定會(huì)比我更吝嗇速度。

            實(shí)踐證明,使用了定制點(diǎn)陣字體文件的方式后,不使用顯示列表而是實(shí)時(shí)從內(nèi)存取得字模再逐個(gè)glBitmap,其效率幾乎可以和使用了顯示列表的內(nèi)嵌Freetype2的字體系統(tǒng)媲美。至于怎么建立自己的字體文件嘛,我的意見是:怎么方便怎么建,讀著方便,用這方便就OK了,因?yàn)橄襁@樣的位圖數(shù)據(jù)生成文件后數(shù)據(jù)是很“稀疏”的,很容易壓縮和解壓,所以空間上不必太擔(dān)心(我自己做的24×24點(diǎn)陣字體文件,連帶額外數(shù)據(jù)只有4MB多一點(diǎn))。

            其他的就沒什么可說的了,要注意的只有三點(diǎn):你需要一個(gè)有序的code-index表,為什么要有序?因?yàn)榇a域很長(zhǎng)而實(shí)際的可顯示碼點(diǎn)很稀少,在一個(gè)有序的靜態(tài)表中二分查找是不二之選;你還需要為每個(gè)字模數(shù)據(jù)建立一個(gè)字模信息記錄,記錄啥?寬width、高h(yuǎn)eight、列步進(jìn)長(zhǎng)度advance、行字節(jié)數(shù)pitch、字模數(shù)據(jù)指針等; 還有就是字模數(shù)據(jù),如果你想更塊一些,讓每行像素的字節(jié)數(shù)擴(kuò)充到4的倍數(shù),浪費(fèi)些空間可以再換些速度。

             

            到目前為止我們基本完成了下面的要求:

            1. 速度快,永遠(yuǎn)不能放棄對(duì)它的追求!

            2. 省內(nèi)存,CPU內(nèi)存要省,GPU內(nèi)存更要一省再省!

            3. 美觀,字是拿來(lái)看的,辛勤勞動(dòng)不能僅因一個(gè)難看而被淪為劣質(zhì)產(chǎn)品。

            4. 簡(jiǎn)單,方法要簡(jiǎn)單通用!這個(gè)好像差點(diǎn)事.....

            5. 支持海量中文,在新一輪的‘文字改革’到來(lái)之前,這永遠(yuǎn)是個(gè)艱巨的任務(wù)!

            http://tsuui.is-programmer.com/posts/4252.html

            posted on 2015-11-07 08:05 zmj 閱讀(2178) 評(píng)論(0)  編輯 收藏 引用


            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            狠狠色丁香久久婷婷综合_中| 97久久婷婷五月综合色d啪蜜芽| 国产福利电影一区二区三区久久久久成人精品综合 | 丰满少妇人妻久久久久久4| 久久久久亚洲精品天堂久久久久久 | 日本精品久久久久中文字幕| 国产精品99久久久久久www| 久久午夜无码鲁丝片秋霞| 久久九九有精品国产23百花影院| 久久午夜福利电影| 99久久精品国内| 免费精品久久天干天干| 国产福利电影一区二区三区久久久久成人精品综合 | 怡红院日本一道日本久久 | 久久久精品国产亚洲成人满18免费网站| 伊人久久大香线蕉成人| 久久青草国产精品一区| 日韩精品久久久肉伦网站| 久久人妻少妇嫩草AV无码蜜桃| 国产国产成人精品久久| 久久综合亚洲色HEZYO社区| 久久精品国产亚洲7777| 韩国三级大全久久网站| 久久青青草原亚洲av无码app| 久久精品国产黑森林| 国产精品女同一区二区久久| 国产精品天天影视久久综合网| 99精品国产综合久久久久五月天| 午夜视频久久久久一区 | 亚洲午夜无码久久久久| 亚洲另类欧美综合久久图片区| 精品久久久久久99人妻| 99久久国产综合精品成人影院| 久久国产热精品波多野结衣AV| 狠狠色婷婷久久一区二区| 色狠狠久久综合网| 久久影视国产亚洲| 天天综合久久一二三区| 香蕉久久夜色精品国产2020| 亚洲午夜精品久久久久久浪潮| 欧美与黑人午夜性猛交久久久|