創作內容

6 GP

貼圖壓縮 - Block Compression實作

作者:巧克力喬斯達│2023-08-07 19:05:59│巴幣:1,010│人氣:98
貼圖壓縮, 在遊戲裡相當重要的功能之一
不單單只是減少記憶體/硬碟使用容量而已
渲染速度也會受惠於貼圖壓縮, 雖然GPU因此多了解壓縮的動作
但解壓縮的計算仍快於存取未壓縮的記憶體頻寬

而今天來寫寫經典的Block Compression, 它是一種破壞性壓縮
參考網站1 (微軟)
參考網站2 (Open GL wiki)

1. Graphics API如何使用BC貼圖
假設已經有BC壓縮過的貼圖, 不論是自己寫壓縮還是用外部工具
首要都是先建立正確的貼圖格式 (以BC1為例)
D3D12: DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_BC1_UNORM_SRGB
Vulkan: VK_FORMAT_BC1_RGB_UNORM_BLOCK, VK_FORMAT_BC1_RGB_SRGB_BLOCK

建立好相對應的資源後, 把raw data複製過去即可, 解壓縮的部分GPU會自己處理

2. 該使用哪個BC格式呢
目前主要有BC1~BC7, 可針對不同場合作使用

BC1: 以壓縮RGB為主, 可以有0個或1個bit來處理Alpha通道. 是的, BC1也可以要求為RGBA, 但是1個bit意味著Alpha值不是開就是關(0或255), 故實用性不是很大, 還是以RGB為主

BC2: 和BC1差不多, 但alpha到了4 bits, 由於BC3的存在此種格式顯得很尷尬
BC3: 和BC1差不多, 但alpha有著8 bits, 最適合用來壓縮RGBA的貼圖
BC4: 適合用來存單一通道(8 bits), 純灰階圖可以考慮, 但在使用時要注意是用R通道而非A

BC5: 適用於雙通道 (各8 bits), 法線貼圖就是它的完美應用, 為什麼呢?由於法線貼圖數值基本上是正規化過的向量(總和為1), 所以只需要存兩個通道, 第三個通道的值用簡單的平方根就能求得

BC6H: 存16bits float RGB, 由於是為了HDR而存在所以名字多了一個H (絕對不是Hentai)
很顯而易見的, skybox的cube map很適合使用

BC7: 最複雜的格式, 特點是儲存了"mode bits", 可以根據場合來決定想要的壓縮動作!
總共有8種模式可以使用, 簡單來說它企圖結合1~6的優點並給予最大彈性

3. BC1、BC3的資料結構
那麼最近在自己的小引擎裡, 加入了BC1 BC3壓縮, 基於練習目的, 當然是自己寫壓縮了
首先看一下BC1的資料結構:

在這個結構中, 它儲存了兩個色彩值(RGB565), 以及16個2-bit的索引值, 總和8 bytes
也就是說每4x4個像素的區塊, 與未壓縮的資料比(16 * 3 bytes), 只要六分之一的容量!

解壓縮時, GPU會讀取這兩個參考顏色, 然後根據索引來決定色彩輸出的動作
所以資料上的儲存是相當簡單明瞭的, 主要問題在於如何挑選參考點
兩個參考顏色深深地影響著最後的結果

而BC3的資料結構長這樣:
RGB的部分與BC1完全一樣, Alpha的部分存了兩個參考點(各1 byte), 然後存了16個3-bit的索引來查找Alpha值, 所以資料總和16 bytes, 也就是說與未壓縮的資料相比(16*4 bytes), 能省下四分之一的容量

主要還是怎麼選擇Alpha的參考點, 但Alpha的索引資料量比RGB還充裕, 選擇上可以單純點
而解壓縮時, Alpha的部分會這個規則來內插數值:

4. BC1、BC3實作
BlockCompressionAlpha()主要用來處理Alpha
BlockCompressionColor()則是RGB
索引的部分很單純, 根據解壓縮的規則, 算出alpha2~alpha7以及color2~color3
而當前區塊裡的每一個像素都跟這些顏色去比, 計算距離最近的索引

Alpha參考點選擇:
由於Alpha部分線性內插的數值高達8個, 單純選擇該區塊裡的min/max顏色就足夠了
09/01更: 後來實作BC4/BC5時發現,單純選最大最小還是有可能破壞邊緣那些像素,所以還是用了兩兩找最佳的方法

Color參考點選擇:
很遺憾, 對RGB來說min/max並不適用所有情況
試想一種極端情況 (白-紅-黑), 這時候最大最小必定取得(255,255,255), (0,0,0)
不論怎樣線性內插, 紅色就是被吞掉了, 只有該區塊剛好存在兩種顏色時效果最好

所以問題可以描述成, 找一組參考點, 讓區塊中所有顏色跟這組參考點有著最短距離
這不是就線性回歸問題嗎?

而在小引擎裡, 我實作了"簡化"過的版本
EvaluateBC1(const UHColorRGB BlockColors[16], const UHColorRGB& Color0, const UHColorRGB& Color1, float& OutMinDiff)

EvaluateBC1的最後一個參數OutMinDiff就是區塊中所有顏色跟參考值的差距加總
那我的測試就是兩兩所有點都呼叫過EvaluateBC1一次, 取差值加總最小的結果作為最終結果

本來一開始我也真的有去寫另一個函式, 然後兩點形成一條線, 以外積得到平行四邊形面積再除以線段長的方式取得最短距離, 但後來想想這跟我直接去窮舉BC1有何分別?
複雜度都是15+14+13+12+...+1次, 而且單純評估最小BC1距離比算外積快多了

而程式的最後, 我還是有去評估min/max顏色的結果, 回到前面所說的
如果一個區塊真的只存在兩種值端顏色, 線性回歸反而不會得到最佳解
想像一下有個區塊8個黑色, 4個暗灰, 4個綠色, 那麼線性回歸很高機率回傳黑色/暗灰做為參考值
那綠色就直接掰啦

5. 壓縮結果

而原圖長這樣:
這張圖是典型的Texture Altas, 在盾牌跟旗幟佔據了貼圖那麼小一部分的情況下
壓縮前後的結果卻看不太出來顯著差異, 更不用說整張貼圖都只為了單一物件的狀況了
可見Block Compression確實是很值得使用, Unreal更是直接把BC1作為預設(他們用的是第三方軟體OodleTexture)

(完)
引用網址:https://home.gamer.com.tw/TrackBack.php?sn=5770359
All rights reserved. 版權所有,保留一切權利

相關創作

留言共 1 篇留言

Toyakoyo
好文! 雖然BC我看了好多次,還是永遠記不及來各種差別,每次都要查XD [e31]
Texture Compression我感覺未來會越來越向neural texture compression靠攏,
今年看到God of War用在Production了
https://schedule.gdconf.com/session/machine-learning-summit-real-time-neural-texture-upsampling-in-god-of-war-ragnarok/893142
Nvidia應該也在搞硬件支持吧

08-13 01:09

巧克力喬斯達
毫無意外是時候丟給AI算了08-13 01:44
我要留言提醒:您尚未登入,請先登入再留言

6喜歡★ltes50414 可決定是否刪除您的留言,請勿發表違反站規文字。

前一篇:Unheard Engi... 後一篇:Unheard Engi...

追蹤私訊切換新版閱覽

作品資料夾

colanncolann
想找發明、小說、繪圖、漫畫、動畫、配音、音樂、模型的創作者們,一起交流、交友、交往! >.0看更多我要大聲說昨天22:34


face基於日前微軟官方表示 Internet Explorer 不再支援新的網路標準,可能無法使用新的應用程式來呈現網站內容,在瀏覽器支援度及網站安全性的雙重考量下,為了讓巴友們有更好的使用體驗,巴哈姆特即將於 2019年9月2日 停止支援 Internet Explorer 瀏覽器的頁面呈現和功能。
屆時建議您使用下述瀏覽器來瀏覽巴哈姆特:
。Google Chrome(推薦)
。Mozilla Firefox
。Microsoft Edge(Windows10以上的作業系統版本才可使用)

face我們了解您不想看到廣告的心情⋯ 若您願意支持巴哈姆特永續經營,請將 gamer.com.tw 加入廣告阻擋工具的白名單中,謝謝 !【教學】