爲什麼需要紋理壓縮

轉:https://www.cnblogs.com/fuckgiser/p/5497013.html

“最近在玩什麼遊戲,推薦一個”。不管是誰,總會說過或聽過這個問題吧。

這時候,你腦海裏面浮現的也許是這樣的畫面

1

或許最終小夥伴們也能接受這樣的遊戲

3

但還是會有一些玩家更懷念這樣的遊戲

2

在軟件開發,特別是三維應用中,紋理隨處可見,但受限於網絡環境和硬件能力,紋理也是一大瓶頸。而且在一般的三維應用中,紋理所佔大小基本都會在1/2以上,模型中往往超過2/3。或許你會說,紋理不就是一張圖嗎,有那麼重要嗎?

如下兩張對比圖,可能你會認爲前者逼格高,但對於正常人而言,後者顯然要好很多。正是有了紋理,如同在骨架上賦予了皮膚,讓我們的應用更加的逼真,貼近現實。

4

而你能想象到嗎?如上的模型一共有三張紋理,其中之一效果如下:

5

看上去怪怪的。其實在紋理的壓縮中,人們先想到了如何去除冗餘信息,對稱的部分只保留一份,儘可能讓不同的部分緊湊,充分利用好每一個像素來保存有效數據。得益於對稱在大自然中的普遍性,這種方式確實極大的減少了紋理像素。

紋理的拼接是紋理壓縮的開始,採用不同的壓縮方式對紋理最終的大小影響也是顯著的。比如上面的這張紋理在不同壓縮格式下的大小差別也是非常顯著的(原始文件爲tga格式,通過Photoshop轉換爲其他格式,默認選項):

6

如果按照上面表格的邏輯,用jpg或png格式就好了,無損壓縮,而且也是最小的。確實在很多情況下這是一個比較好的選擇,比如網絡帶寬有限,這樣可以很好的節約帶寬和下載時間,而Bmp,tga,png以及jpg都是無損格式。但這類壓縮存在一個致命缺陷,他們都是基於整幅圖片下進行的壓縮,比如霍夫曼編碼等,這樣像素和像素之間在解碼的過程中存在依賴關係,無法直接實現單個像素級別的解析,這就發揮不了顯卡的併發能力,更重要的是問題在於無論是png還是jpeg最終在顯存中解碼後都是RGBA的紋理格式,因此並無法減少顯存的佔用率。比如一張256*256的RGBA紋理,無論是png還是jpg格式,雖然文件大小不一樣,在顯卡中的大小仍然是256*256*4的顯存空間。

正是因爲傳統的圖片格式並沒有考慮顯卡的這種特性,所以很難滿足三維應用中的要求。基於這些問題,如下四點可以作爲我們選擇紋理壓縮格式的衡量標準(引自《Texture Compression using Low-Frequency Signal Modulation》)

  • 解析速度
    在紋理操作中,讀取紋理數據是關鍵步驟,所以解碼速度至關重要。

  • 隨機讀取數據
    能快速的隨機讀取任意像素

  • 壓縮率和紋理質量
    既要保證一個不錯的壓縮效果,也要把紋理損失控制在一定範圍內

  • 壓縮速度
    通常紋理壓縮在渲染前已經提前準備好,所以如果壓縮的速度比解析速度慢,也是可以接受的。

調色板技術

最初想到的就是調色板技術,這個思路很簡單,在當時硬件能力不高的情況下也非常的好用,類似GIF格式,通常保存一個8位或4位的調色板。爲什麼沒有16位的調色板,因爲16位的RGB的效果本身就相對不錯,所以16位調色板的意義並不大。如下是調色板原理示意。

7

對於紋理中顏色個數不超過256,或者願意適當刪減,將顏色數目控制在256以內的話,調色板還是非常高效的壓縮技術,相比RGBA的顏色格式要少87.5%的空間。當然,顏色越豐富,所效果損失越嚴重。如下是同一張紋理的效果對比:

8

調色板方式下還有一個非常明顯的優勢是風格的變化,只需要更改調色板信息,而不用保存多套紋理,就可以很輕鬆的實現風格的多樣化,這種成本很低,而且還很高效。

9

但是顯卡中並不支持這種調色板紋理方式,或者只有很老的顯卡會支持,當然我們可以採用一維紋理的方式來模擬調色板,但這種情況下不能開啓紋理過濾功能,因爲調色板的顏色順序是隨機的,在插值過程中和我們預期的效果會有出入。

10

可見調色板的使用非常靈活,如果運用得當,很多複雜的問題都可以很好的解決,但畢竟顯卡不支持這種紋理方式,而且畢竟也有256顏色的限制,在稍微複雜的情況下就有點捉襟見肘。而在頂點着色器上,每次都要操作兩次(獲取索引值,讀取調色板對應的顏色),而且調色板也需要作爲參數,或指定一個全局的調色板,這樣就會存在內存和顯存之間的頻繁切換,從性能的角度來也不是最優方案。

紋理壓縮

調色板方式有着很多的優點,但也有致命的缺點,在調色板的基礎上不斷改良,最終形成了現在這種相對平衡的壓縮方案。

11

        首先,意識到有損壓縮下的顯示效果還是不錯的,所以壓縮後以16位的顏色格式存儲,如上是RGB和16位的對比效果圖。再次則是自帶“調色板”,化整爲零,方便自身攜帶。

12

上圖是紋理壓縮原理圖,對於一張原始紋理,會創建兩張小紋理A和B,可以認爲是原始紋理的縮略圖,同時還有一個矩陣M,M的行列和原始紋理的長寬一致,裏面的值類似於調色板中的索引,實現紋理A和紋理B的混合。示意圖如下:

13

可以說目前主流的紋理壓縮格式,比如DXT,PVR和ETC紋理壓縮在原理上如出一轍,但在細節上會有很多獨特的改善。我們逐個道來,對比其中的優劣。

DXT

       DXT是一種有損紋理壓縮算法,微軟的Direct中支持,DXT的格式包括DXT1~DXT5,其中DXT1和DXT5較爲多見,後面會做詳細討論。可以說DXT是目前應用最廣泛的紋理壓縮格式,可以認爲所有的PC端顯卡都支持DXT壓縮,維基百科說記錄,該專利有效期到2017年10月2號。

14

如上圖,DXT的壓縮思路也比較一致,有兩個Color A(00)/B(11),而4*4矩陣中的索引比較簡單,在DXT不同的格式中,差值的因子稍有不同,比如在DXT1中,差值得到的另外兩個顏色的公式爲:C2=  2/3*C0 + 1/3*C1, C3 =  1/3*C0 + 2/3*C1,這裏有一個小技巧,儘管分母是3,但都會近似到2的N次冪,比如2/3約等於5/8,爲什麼?如下是食人怪紋理的DXT1效果:

15

DXT算法非常容易理解,而且整體看上去效果不錯,但如果對局部特寫,會發現在細節上會有很多丟失,這也是算法本身導致的,畢竟每個塊只有兩個顏色,而其他顏色都是在這兩個顏色區間的差值,如果當前區域內還有其他顯著顏色則必然會有丟失。

16

這種信息的丟失主要集中在比較細的邊界中,但DXT1在壓縮率上是RGB的6倍,這種問題可以通過提高紋理分辨率的方式來解決,高寬放大41%,這樣整個紋理是以前的2倍,但壓縮率還能保持爲3倍,也是可以接受的。

在DXT中還有一個主要的損失,就是RGB的24位轉爲了16位顏色,16位中R&B各佔5位,但是G佔了6位,這是因爲人眼對綠色最爲敏感。

17

另外一個問題就是DXT3和DXT5之間的對比,相比DXT1不支持透明度(但支持是否透明),DXT5要大一倍(多了64bit),和之前顏色保存方案一樣對透明度也保存了兩個16位的顏色和對應的調色板,對RGBA的效果也得到了保證,但DXT3思路不一樣,它是對每一個像素保存了4bit的透明度,同樣也是多了64bit,但此時畢竟只有16個透明度選項,相比DXT5,在壓縮率上相當,但對透明色的處理不夠細膩,因此在實用性上並不推薦DXT3。

儘管DXT在細節上有明顯硬傷,在總體效果不錯,而且確實是一種強大的壓縮方式,所以在多數紋理壓縮選擇中都是最佳方案,幾乎可以認爲是PC下的標準壓縮格式。

PVR&ETC

也許是出於專利和商業角度,也許確實DXT在移動端確實無法滿足要求,DXT並沒有在移動端得到很大的支持,相反,在iOS設備中支持的是PVR壓縮,在Android中支持的是ETC壓縮。下面詳細介紹一下PVR的壓縮和ETC解壓縮的過程。

       DXT在細節上缺陷明顯,最重要的原因是當把紋理分爲4*4像素的區域塊後,每個塊之間都是獨立的,儘管這極大的簡化了壓縮算法,但卻丟失了相鄰塊之間這種普遍的相似性。這是算法本身導致的,而PVR則會考慮該區域塊對應的右側,下側和右下側的三個區域塊的關聯性。

18

如上,是PVR中一個block的結構圖,同樣的兩張Color A/B,這裏會有透明模式和非透明模式,這種策略是可取的,首先不用單獨增加透明度的字段,儘管透明度的增加會犧牲RGB的質量,但在透明情況下,RGB的作用並不如在不透明情況下那樣重要,這種損失也是可以接受並彌補。

首先,在生成Color A/B時,會對原始紋理做一些處理(最簡單的就是翻轉或旋轉90度),得到一張delta image,這個delta可以用來調整生成的Color A/B,比如最簡單的一個方式就是點乘每個delta向量。

然後基於Color A/B來計算該block中對應的M,相比於DXT1中的1/3和2/3,PVR中對應的值分別爲:

19

在計算M的過程中,會對已有的Color A/B進行優化,這個過程稱爲SVD(Singular ValueDecomposition),因爲非常耗時,時間複雜度是O(n^3),所以僅對中心塊進行優化,而對四周不進行此操作。

20

雖然PVR也提供了2bpp(bits perpixel)的格式,但效果很爛,所以基本只有4bpp是可用的。另外因爲考慮了區域塊之間的相關性,還有SVD算法對效果的優化,不難想象,PVR紋理在壓縮時性能慢的難以忍受。但沒辦法,畢竟PVR是iOS官方支持的紋理壓縮,只能忍。

上面是PVR的壓縮過程,下面我們對應的看一下ETC下解壓縮的過程。

21

如上,是ETC中Color A/B的大致分佈(其中一種較爲簡單的情況,總共兩種情況,取決於diffbit是0還是1)。而cw1和cw2對應所需的“調色板”。

在ETC中,對調色板做了一個優化,下面是索引和值的對應關係:

22

這個調色板並不複雜,結合M中對應的索引(2bit),獲取每個原始像素對應Color A/B的偏移量

23

如上是ETC的解壓,至此,我們詳細介紹了三種主流的壓縮格式,他們之間思路相仿,但具體細節上各有所長,爲了壓縮的性能,可以說裏面有很多小的技巧值得我們借鑑。當然孰優孰劣,每個人或許都會有自己的判斷。下面是一個詳細的各種壓縮格式下效果和壓縮比的對比圖,各位感興趣的可以自行對比。

 

24

25

 

總結

終於寫完了,本文主要參考資料和圖片來自joostdevblog中對DXT和調色板的詳細介紹,以及上面提到的Texture Compression這篇經典論文,以及維基百科中關於DXT紋理壓縮,以及OpenGL官網上對ETC紋理的介紹。在學習紋理壓縮這個過程中,也在思考這樣設計的目的,不同紋理之前的細微區別,究竟是商業因素還是技術問題,導致了這些差異,也在試圖總結,這麼多的trick他們是怎麼想到的,雖然看起來不起眼,但裏面都是經驗和智慧的結晶,整合在一起確實讓人歎服,個人也在不斷的梳理方方面面,希望能通過自己的理解,能讓大家有條理的,相對深入的瞭解紋理壓縮的原因,更好的理解運用這些技術。當然,難免有出錯的地方也希望指正。

但從現實的角度來看,受制於專利和硬件廠商,我們並沒太多選擇的餘地,Android下就要用ETC,iOS下只能PVR,而在PC上不用DXT估計就要被嘲諷了。但這也是一個很棘手的問題,比如在WebGL下,特別是Android下差異化很大,是否支持紋理壓縮,甚至在同一個設備不同的瀏覽器,因爲驅動的不一致,可能系統自帶的會支持ETC壓縮,而微信等QQ瀏覽器下並不支持。而且華爲的手機貌似在瀏覽器級別下都不支持ETC(硬件支持,還是驅動的問題)。而如果在移動設備上不用壓縮,顯存是有限的,除非你在數據量上做出犧牲,怎麼解決都很矛盾,相比而言,iOS下則要舒服很多。設備的多樣性帶來的煩惱,讓我覺得喬布斯的偉大之處:優秀的同事不是爲了計算機而工作,而是因爲計算機是傳達某種情感的最佳媒介,他們渴望分享,正是因爲這種精神,有些人寧願做詩人也不願意做銀行家,我想把這種精神溶入產品裏,而計算機就是我傳達情感的媒介。

好了,雞湯不是白喝的,長按下圖有奇蹟,轉發更能保平安~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章