electronic_blue

用了 octopress 就沒理由再不寫 blog

使用 RGBA4444 與 Dithering 減少記憶體用量

|

在 2D 遊戲中,圖片一向在消耗的記憶體中占用最大的比例。尤其在記憶體資源寶貴的行動裝置上,為了減少記憶體使用量,縮減圖片的大小常常是最有效的方法。

大部份的遊戲會使用材質壓縮 (texture compression) 來縮小圖片的記憶體用量,而在這篇文章中,我會介紹另一種方法:使用 RGBA4444 的材質格式,並配合 dithering 維持圖片品質。

關於圖片壓縮

一般我們常用的圖片格式是 JPEG 或 PNG,這兩種都是已經壓縮過的圖片格式,而且壓縮率算是相當好。然而它們所使用的壓縮演算法並不適用在即時繪圖的場合中,因此當遊戲引擎要畫出 JPEG 或 PNG 圖檔時,會先把每個像素解壓縮至記憶體中,而無法節省記憶體空間。

為了解決圖片占用過多記憶體的問題,顯示卡晶片廠商提供了材質壓縮的功能。只要在記憶體中存入特定格式的壓縮圖片,顯示卡晶片可以在取出像素的同時動態解壓縮,而不需要把整張圖片解壓縮,這麼一來就可以節省空間了。

在行動裝置上,常見的材質壓縮格式如下:

  • ETC1:OpenGL ES 2.0 標準所規定的壓縮格式,不支援 alpha channel,但幾乎所有裝置都支援這種格式。
  • DXT:桌機或遊戲機上常用的壓縮格式,由 NVIDIA 的 Tegra 晶片使用。
  • ATITC:由 ATI 提出的壓縮格式,由 Qualcomm 的 Adreno 晶片使用。
  • PVRTC:由 Imagination Technologies 提出的壓縮格式,主要由 PowerVR 的晶片使用。(iPhone/iPad 或 PS Vita 都使用 PowerVR 的顯示晶片)

使用材質壓縮可以有效地縮減記憶體用量,其壓縮率達 ¼ 至 1/8 之間。但這並非完美的解決方案,其問題如下:

  1. ETC1 不支援透明度,但透明度在遊戲中極為重要,因此需要偵測顯示晶片的支援能力,載入不同格式的壓縮圖片,而這在某些遊戲引擎中非常麻煩(對,我說的就是 Unity)。
  2. 雖然在 iOS 上可以統一使用 PVRTC 這個格式,但 PVRTC 對於有透明度的圖片所造成的破壞又特別嚴重。下面右圖為使用 PVRTC 的例子,點開原圖即可發現邊緣有明顯的雜訊。
  3. 有些平台會因為專利因素無法支援材質壓縮,比如說 PlayStation®Mobile。
原圖
原圖
PVRTC
使用 PVRTC 壓縮

所幸,當材質壓縮遇到困難時,我們還有一些別的方法。

使用 RGBA4444

然而為了節省記憶體空間,繪圖 API 通常提供了 RGBA4444 這種材質格式--每個色彩通道僅使用 4bit 來儲存資料,占用的記憶體即為原本 8bit 的一半。

在 Unity 中,只要選取一張圖片,即可設定它的材質格式。然而,若直接設定為「RGBA 16 bit」,雖然占用空間減為一半,但會出現明顯的顏色誤差。這現象在鉛筆頂面尤其明顯,因為人眼的感光細胞對綠色特別敏感,很容易就發現原本的漸層色都跑掉了,變成一階一階的綠色。

原圖
原圖
RGBA 16bit
RGBA 16bit

我們可以用 dithering 來消除這種現象。

Dithering

Dithering 意指把不同顏色的像素交錯排列以營造出混色的錯覺。在以往記憶體極小,螢幕顯示色數又只有 256 甚至 16 色的時代,遊戲幾乎都會使用 dithering 提高畫面的品質。右圖就是 dithering 的例子,這張圖片只用了黑百兩色,但運用交錯排列的效果營造出灰色漸層的錯覺。

我們可以應用同樣的原理來處理圖片,我這邊使用的是 GIMP 這套強大的影像軟體,搭配 16-bit dither script 外掛。這些工具都可以免費下載使用。

安裝好之後,使用 GIMP 打開圖片檔,選擇「圖片」→「模式」→「Dither to ARGB4444」即可進行 dithering。處理後的圖片如下:

原圖
原圖
RGBA 16-bit Dithered
RGBA 16-bit Dithered

經過 dithering 後的圖片和原始圖幾乎看不出差異了,而占用的空間也能縮減至一半。

結語

原則上,若材質壓縮沒有太大的問題時,就應該使用材質壓縮,因為它能最有效地節省記憶體。然而遇到跨平台或是需要 alpha channel 時,使用 RGBA4444 搭配 dithering 則是很優秀的方案,尤其現在 retina display 相當流行,因為在 ppi 超過 300 的螢幕上幾乎看不到 dithering 的顆粒,它看起來和原始圖片是一模一樣的。

迴響