幾種貼圖壓縮方式詳解


一、前言
    遊戲場景裏,貼圖是影響真實性的重要因素。通常貼圖越大,也就越精細,但其佔用的內存空間也就更大。
貼圖大小 16 bits 16 bits mipmap 24 bits 24 bits mipmap 32 bits 32 bits mipmap
64x64 8 KB 10.6 KB 12 KB 16 KB 16 KB 21 KB
256x256 128 KB 170 KB 192 KB 256 KB 256 KB 340 KB
1024x1024 2 MB 2.6 MB 3 MB 4 MB 4 MB 5.3 MB
    常用的圖片文件格式有:BMP, TGA, JPG, GIF, PNG等。
    不過,像JPG這種常見的圖片壓縮格式,對於多數應用的內存佔用和顯示總線帶寬佔用並沒有帶來直接的好處,因爲還得對JPG進行解壓縮成原始的像素,再傳給顯卡,而且還有加載時的解碼計算負擔。這是因爲顯卡的紋理解碼硬件不理解JPG格式。所以,在沒有顯卡硬件支持的情況下,用壓縮格式保存紋理沒有什麼意義,特別是對於移動設備來說,解碼像JPG這種複雜格式是很浪費電的。
    常用的紋理格式有:RGB_565, ARGB_4444, ARGB_1555, RGB_888, ARGB_8888等。
    不論何種圖片文件格式,它們都是爲了存儲像素信息而是用的對信息的特殊編碼方式,它存儲在磁盤中,或者內存中,但是並不能被GPU所識別,因爲以向量計算見長的GPU對這些複雜的計算無能爲力。當這些文件格式被遊戲讀入後,需要經過CPU解壓成RGB_565,ARGB_4444, ARGB_1555, RGB_888, ARGB_8888等像素格式,才能傳送到GPU裏使用。紋理格式是能被GPU所識別的像素格式,能被快速的尋址並採樣。
    紋理格式如:RGB_565,每個像素佔用:5+6+5=16 (bits),共 2 個字節。RGB_888,每個像素佔 24 位,3 個字節。ARGB_8888佔 32 位,4 個字節。
    關於紋理格式的更多資料:http://en.wikipedia.org/wiki/High_color


二、貼圖壓縮方式
    對於一張 512*512 的紋理,RGB_565格式的文件佔用 512 KB的容量。
    計算公式爲:
numBytes = width * height * bitsPerPixel / 8
    ARGB_8888格式的文件需要佔用1M的容量;如果是1024*1024,則需要更多。
    (以下內容直接摘抄:)
現在一般的顯卡上通常有 32MB 的顯存容量。如果每個貼圖都要 2MB 的話,即使不計 frame buffer 所佔用的空間,也只能使用 16 張貼圖。這顯然是不可接受的。所以,現在的遊戲通常無法使用很大的貼圖。

然而,在儲存一般的影像的時候,通常會使用某些壓縮方式。現在常見的 JPEG 壓縮,可以達到 1:6 甚至 1:12 的壓縮比。如果把類似的壓縮方式應用在貼圖上,不就可以大量減少貼圖所用的空間了嗎?

不幸的是,一般的影像壓縮方式,是沒有辦法用在貼圖上面的。因爲,顯示芯片在存取貼圖時,是一種「隨機存取」的動作。也就是說,顯示芯片通常會需要以任意 的順序存取貼圖裏的資料。一般的壓縮方式如 JPEG,都利用了 variable length 的 coding,簡單的說,它們必需以一定的順序才能解開。因此,不能用這種方式來壓縮貼圖。

一種壓縮方式,是改變顏色空間。例如,3dfx 的 YAB 格式,就是一種不同的顏色空間。利用 YAB,每個像素只需要 8 bits,就可以達到接近 16 bits 的效果。不過,無論如何,這樣都使顏色的數目減少。因此,整個貼圖的色彩變化就受到了限制。

另一種方式,就是用傳統的「調色盤」結構。利用一個 256 種顏色的調色盤,就可以把貼圖以 8 bits 的方式儲存。不過,雖然它的色彩空間較大(可以是 24 bits 或 32 bits),但是總顏色數目還是不能超過 256 種。所以,它的應用範圍仍然有限。

現在常用的貼圖壓縮方式,則是利用以區塊爲基礎的方式。通常的做法是,把貼圖切割成許多小區塊,再對各個區塊進行壓縮。例如,S3TC 就是把貼圖切成 4x4 的小區塊。利用這種做法,就可以對區塊進行某種處理(通常就是 vector quantization 或是其變形),顯示芯片也可以以區塊爲單位,進行隨機的存取動作。因此,這是適合用在貼圖的方式。

不過,區塊的大小會影響到壓縮的效果。一般來說,區塊越大,就能有越高的壓縮比。不過,越大的區塊也會使額外的負擔增加。因爲顯示芯片只能以區塊爲單位來讀取貼圖,如果區塊越大,則每個區塊中就可能會有越多的資料是不需要的。所以,也不能任意把區塊的大小加大。
    在Beers,Agrawala和Chaddha於1996年發表的一篇影響深遠的論文基於已壓縮紋理的渲染 [1]中,他們列舉四項紋理壓縮的特點,使其不同於其他圖片壓縮技術。
  • 解壓速度:爲了儘可能不影響性能,解壓縮要儘可能快,最好能直接從已壓縮的紋理直接渲染。(所謂解壓,就是把貼圖轉換成GPU能識別的紋理格式:RGB_565等。)
  • 隨機訪問:由於幾乎不可能預測紋理像素被訪問的順序,任何紋理壓縮算反必須允許對其中的紋理的隨機訪問。所以幾乎所有的紋理壓縮算法都已塊爲單位壓縮和存儲紋理像素,當某一個紋理像素被訪問時,只有同一塊中的若干紋理像素被讀取和解壓縮。這項需求也排除了很多壓縮率較高的圖片壓縮格式,例如:JPEG行程長度編碼
  • 壓縮率和圖像質量:由於人眼的不精確性,相比於其他應用領域,圖像渲染更適宜使用有損壓縮。
  • 編碼速度:紋理壓縮對壓縮速度的要求不高,因爲絕大多數情況下,紋理只需要進行一次壓縮。(但是對解壓速度要求較高。)
    由於其數據訪問模式是事先知道的,紋理壓縮常作爲整個渲染管線的一部分,在繪製時動態的對已壓縮數據進行解壓縮(可以把解壓縮放在shader裏處理)。而反過來渲染管線也可以通過紋理壓縮技術來降低對顯卡位寬和存儲的需求。在紋理貼圖中,已經壓縮的紋理和沒有經過壓縮的紋理使用起來基本沒有區別,都可以被用來存儲顏色數據或其他數據,例如凹凸貼圖或法線貼圖,也都可以和Mipmapping或各項異性過濾等共同使用。
    主流的紋理壓縮標準:ETC, PVRTC, S3TC。
    (此處略去一段關於三種紋理的歷史介紹)
    下面介紹一些貼圖壓縮方式的細節:

三、VQTC (Vector Quantization Texture Compression)
    先看一種簡單的貼圖壓縮方式:VQTC。
    VQTC是由 NEC/Videologic 的 PowerVR 系列所使用的一種貼圖壓縮方式,但是因爲 PowerVR 系列一直沒有取得很大的成功,所以 VQTC 一直未受到重視。而現在 Videologic 最新的 Kyro 顯卡芯片業不在支持 VQTC。不過,因爲它非常簡單,因此不妨瞭解一下。
    VQTC 使用的是 vector quantization 的方式,所以叫做 VQTC。
    壓縮原理:
    把貼圖切成許多大小相同的區塊(例如,2*2, 4*4),對這些區塊做 vector quantization。例如,可以從這些區塊中,找出4096個最具代表性的區塊。這樣一來,每個區塊就只需要存放 index,也就是 12bits 的空間。
    顯卡芯片在讀取貼圖資料時,要將整個向量(即 4096 個區塊)讀入顯示芯片中的一個向量表。這個動作對每個貼圖只需要做一次。在讀取各個 texel 時,則是先判斷出所需的 texel 所在的區塊(以2*2區塊來說,就是把位置座標分別除以 2),讀取 index,再從向量表中查出區塊的內容。基本上,這些動作都非常簡單。
    在壓縮比方面,以上面的例子來說,如果壓縮 256*256 的貼圖,並採用 2*2 的區塊大小,那麼貼圖就有 128*128=16384 個區塊。找出 4096 個最具代表性的區塊後,整個貼圖需要的控件就是 4096 個區塊再加上 16384 個index的空間,也就是 4096*16+16384*1.5 = 88 KB。和原來未壓縮所需要的 256 KB 相比,壓縮比爲 1:2.9。
    當然可以調整區塊的大小爲 4*4 或者更大,不過,這樣會使的壓縮後貼圖的精度變低。通過調整區塊的大小,和數目,就可以調整出不同精度的壓縮貼圖,這是 VQTC 的優點之一。
    VQTC 的缺點是,不能做部分更新。簡單的說,如果想要更新一個 VQTC 貼圖的一小部分,幾乎說是不可能的,總是需要將貼圖重新壓縮,或是犧牲一些品質,使用原有的向量來壓縮。另外,VQTC 也不太適合處理有透明通道的貼圖。
    VQTC 現在已經不常用了。目前更流行的的貼圖壓縮,則是由 S3 所開發的 S3TC。

四、S3TC(S3 graphics Texture Compression)
    S3TC 是由 S3 公司所開發出來,最早用在它的 Savage 系列芯片中。雖然 Savage 顯示芯片並不算成功,但是 S3TC 卻因爲微軟公司將其加入 DirectX 而變得相當成功。微軟向 S3 授權 S3TC 技術後,其他公司在 DirectX 中支援 S3TC(在 DirectX 中稱爲 DXTC),則不需要再付給 S3 費用,因此得到了相當程度的支持。
    S3TC 包括五中貼圖壓縮格式,在 DirectX 中,分別稱爲 DXT1 ~ DXT5。這些不同的貼圖壓縮格式,其實就是爲了透明度(Alpha Channel)所設計的。
    DXT1 是 S3TC 中,最基本的壓縮格式。它是用來處理 沒有 Alpha Channel 或者是 Alpha Channel 爲 1bit 的貼圖。其他的壓縮方式都只是 DXT1 的變化而已。
    壓縮原理:
    DXT1 所用的方法,和 VQTC 類似,也是將貼圖切成許多小區塊,然後再利用 vector quantization 的方式來壓縮。不過,和 VQTC 不同的是,DXT1 的區塊總是 4*4 的大小,而且它將 vector quantization 用在區塊內,而不是像 VQTC 對所有的區塊做 vector quantization。
    對於每個 4*4 的區塊,儲存了兩個 16bits 的顏色,顏色的格式是 RGB_565。從這兩個顏色,可以用線性插值的方式得到另外兩個顏色。簡單的說,如果這兩個顏色分別是 RGB-0 和 RGB-1,那 RGB-2 和 RGB-3 可以由下面的式子求出:
    RGB-2 = (2*RGB-0 + 1*RGB-3)/3;
    RGB-3 = (1*RGB-0 + 2*RGB-3)/2;
    這樣就得到了四個顏色。區塊中的每個 texel 則是一個 2bits 的 index 表示。如果 index = 0,則表示這個 texel 的顏色是 RGB-0;index = 1,則是 RGB-1;index = 3 則是 RGB-3。以這樣的方式存儲,每個區塊只需要 16*2 + 2*4*4 = 64bits。因此,在壓縮 16bits 的貼圖時,壓縮比是 1:4.
    DXT1 同時也可以處理 具有 1bit Alpha Channel 的貼圖。一般來說,在貼圖中使用 1bit 的 Alpha Channel,就是用來註明透明的地方。因此,DXT1 的處理方式,是犧牲了1個顏色,用它來表示透明的 texel。
    如果一個區塊需要存放透明度的話,那麼,顏色的產生方式就變成:
    RGB-2 = (RGB-0 + RGB-1)/2
    和前面一樣,texel 的 index 仍是 2bits。index = 0~2 時,分別是表示 RGB-0~RGB-2。當 index = 3 時,就表示這個 texel 是透明的,它的顏色是黑色,且 alpha = 0。
    不過,要怎麼區分 有 Alpha 和 沒有 Alpha 的情形呢?DXT1 使用一個簡單的方法:如果 RGB-0 比 RGB-1 要大的話,那就表示這個區塊是沒有 Alpha 的。相反,則表示有 Alpha Channel 了。
    當 Alpha Channel 不只是 1bit 的時候,就需要其他的方式了。DXT2~DXT5 就是在處理這種情況。因爲它們的原理和 DXT1 是相同的,所以就不再多做介紹了。不過,DXT2~DXT5 每個 4*4 區塊需要 128 bits,也就是說,他們的壓縮比只有 DXT1 的一半。
    S3TC 的優點:因爲它是針對每個 4*4 的區塊做壓縮,所以可以對貼圖做 部分更新 的操作。要更新貼圖中的某個部分,只需要更新這部分的區塊即可,其他區塊完全不會被影響。

五、FXT1(3dfx Texture Compression)
    基本上,FXT1 和 S3TC 是非常類似的。只不過,FXT1 使用 8*4 的區塊,而非 S3TC 的 4*4 區塊。而且相對於 S3TC 只有一種壓縮方式(即DXT1,DXT2~DXT5 只是將 DXT1 加上 Alpha Channel 而已),FXT1 有四種不同方式。這使得壓縮的時候,可以針對不同的情形,選擇最適當的壓縮方式。
    FXT1 有四種壓縮方式,都是使用 8*4 的區塊。第一種方式稱爲 CC_HI,用來壓縮使用 1bit Alpha Channel 來表示透明度的貼圖。
    壓縮原理:
    CC_HI 和 DXT1 非常類似,它也是存放兩個顏色,不過,和 DXT1 不同的是,CC_HI 用的是 15 bits 5-5-5,而非 DXT1 的 16 bits 5-6-5。
    利用線性插值的方式,CC_HI 的兩個顏色會插值出 5 個顏色,合起來是 7 個顏色,再加上一個顏色表示 透明texel。每個 texel 則使用 3bits 的 index 來指向這 8 個顏色。另外,CC_HI 使用 2 bits 的 mode bits 來表示這個區塊是 CC_HI 格式。所以,一個 8*4 的區塊需要 2+2*15+32*3 = 128 bits。因此,如果貼圖是 16bits 的話,壓縮比就是 1:4。
    第二種方式稱爲 CC_CHROMA,它是專門用來存放 沒有 Alpha Channel 的貼圖。基本上,CC_CHROMA 的方式很簡單:它存放 4 個 15bits 的顏色,每個 texel 則用一個 2bits 的 index 指向這 4 個顏色。它的 mode bits 是 3bits,還有一個未使用的 bit,所以它需要的空間是 3+1+4*15+32*2=128bits。
    第三種方式稱爲 CC_MIXED,這個模式非常的複雜。它將 8*4 個區塊分成兩個 4*4 的小區塊,每個小區塊有兩個 16bits 5-6-5 RGB的顏色。每個小區塊中的壓縮方式和 DXT1 幾乎相同,但是它不是用顏色的大小來區分 是否有 Alpha Channel,而是用一個 Alpha Bit 來區分。這個模式需要的空間也是 128bits。
    第四種方式稱爲 CC_ALPHA它是用來處理具有多位 Alpha Channel 的模式(類似 DXT2~DXT5)。它使用3個 20bits 5-5-5-5 ARGB格式的顏色,並有兩種模式:第一種模式,每個 texel 的 index(2bits)指向這三個顏色,而第四個顏色則是表示 透明texel。第二種模式,則是將區塊分成兩個小區塊,並使用類似 DXT1 的方式(第一個小區塊用 COLOR0 和 COLOR1 爲基準色,而第二個小區塊則用 COLOR1 和 COLOR2 作爲基準色),只不過有加上 Alpha Channel。這個格式的 mode bit 是 3bits,再加上一個 模式 bit,所以需要的空間是 3+1+20*3+32*2=128bits。
    FXT1 與 S3TC 並沒有很大差別,一般來說,在壓縮 24bits 或 32bits 的貼圖時,FXT1效果會好一點。但是由於其區塊較大,是顯卡芯片在處理時的效率較差。
    FXT1 和 S3TC 都有相對應的 OpenGL extension 來提供支持:
    S3TC:GL_EXT_texture_compression_s3tc
    FXT1:GL_3DFX_texture_compression_FXT1

六、PVRTC(PowerVR Texture Compression)
    PowerVR texture Compression 最早是用於 IOS 系統,用於顯示器芯片支持 PowerVR 的。

    優點:相對png來說,pvr 能更節省一張圖片加載後佔用的內存。
    缺點:缺點是壓縮方式是有損的壓縮方式,因此對於某些圖片不適宜用pvr壓縮,通常pvr格式貼圖會被用於2D的動畫,特效等。

    下面介紹文件格式與壓縮原理。
    1. 文件格式
    PVRTC在OpenGL ES的擴展名爲:GL_IMG_texture_compression_pvrtc,支持預處理壓縮。當加載PVRTC壓縮紋理是,<internal format>參數支持如下格式:
COMPRESSED_RGB_PVRTC_4BPPV1_IMG (RGB 4 bit per pixel)
COMPRESSED_RGB_PVRTC_2BPPV1_IMG (RGB 2 bit per pixel)
COMPRESSED_RGBA_PVRTC_4BPPV1_IMG (RGB 4 bit per pixel with alpha channel)
COMPRESSED_RGBA_PVRTC_2BPPV1_IMG (RGB 2 bit per pixel with alpha channel)
    (敬請期待...)



七、ETC
    (敬請期待...)

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