細談紋理壓縮格式

我們先不談紋理壓縮在做什麼,我們先看下自然狀態下沒有壓縮的紋理的問題

不壓縮的紋理有什麼問題

目前的渲染管線中,對於3D物體表面的細節,主要還是靠紋理貼圖來表現,分辨率越高,精度越高的紋理,在細節表現上自然越強,但是同時會導致內存開銷增大,以及帶寬等問題。

佔用內存過大

對於RGBA四通道的紋理,每像素需要8bits*4=32bits=4Bytes的空間。使用512X512的貼圖,一張圖就需要佔用1M的內存。假設還開啓了Mipmap(多級漸進紋理)還需要額外的1/3大小的空間,也就是1.33M。一個場景如果使用了100張這樣的紋理,單獨紋理的內存開銷就需要133M的內存。如果沒有紋理壓縮,只能通過降低紋理尺寸來減少內存開銷

除了內存還有帶寬 (事實上對於移動端遊戲帶寬相比內存更加緊俏)和磁盤空間

紋理佔用內存大,自然也會導致帶寬開銷變大,與此同時包體大小也會受到影響。而帶寬和包體大小對於移動端遊戲來講都是非常稀缺的系統資源,每多用一分都要額外注意,當然內存也是如此。對於需要適配低端設備或者其他部分內存開銷很大的遊戲,尤其如此。

紋理壓縮在做什麼

不同於我們通常理解上的圖片壓縮(png,jpg等),紋理壓縮是服務於在3D 圖像渲染系統的紋理圖片存儲技術,因此要充分滿足隨機訪問的特性。

紋理壓縮的特性

解壓速度 Decoding Speed

爲了能夠直接從壓縮的紋理中直接採樣數據,解壓速度必須足夠快,而且基本上不能影響到渲染性能

隨機訪問 Random Access

因爲預先知道紋理採樣的順序幾乎是不可能的,任何紋理壓縮技術必須能夠快速隨機訪問解壓過的紋理數據。這就和一些常見的圖片壓縮方法產生衝突,例如JPEG.

壓縮率和紋理質量 Compression Rate and Visual Quality

對於渲染來講,有損壓縮是可以接受的。一些紋理壓縮庫,例如crunch ,允許開發者 靈活的在壓縮率和紋理質量見進行平衡。例如Unity的紋理中就支持在選擇crunch方式時,設置1-100的紋理質量,數值越低壓縮率越高,而紋理的視覺效果也會更差。

壓縮速度 Encoding Speed

紋理壓縮,對於壓縮/解壓速率的極度不對稱是完全可以容忍的,因爲通常紋理壓縮只需要在遊戲打包時進行一次,對於用戶運行時體驗完全沒有影響。


基於以上特性,大多數紋理壓縮算法會把固定大小的像素塊的某種形式的固定速率有損矢量量化(lossy vector quantization )包含進固定大小的編碼字節塊中,有時還會配合一些額外的預處理和後處理步驟。塊截斷編碼(Block Truncation Coding )是這種類型的算法的很常見的一個例子。

因爲他們的數據訪問模式是明確定義的,紋理解壓縮過程是作爲圖像管線的一部分在運行時進行的,這樣可以降低渲染系統對帶寬和存儲空間的需求。除了紋理貼圖,紋理壓縮可以應用於其他類型的渲染貼圖,包括凹凸貼圖和表面法線貼圖。同時,紋理壓縮可以和其他貼圖處理流程共同使用,例如Mip maps和各向異性過濾。

通俗來講,紋理壓縮不同於其他圖片壓縮方式(jpg,png),在使用中,不會在CPU中進行解壓縮,而是直接把壓縮內容傳給GPU,而且在GPU中也不會一次把整張圖片進行解壓縮,只會在需要採樣特定區域的紋理時對這一區域的紋理進行解壓縮。

紋理壓縮技術的優點:
  1. 解決了內存和顯存(GPU的專用內存)中紋理佔用空間大的問題,不需要在CPU中就解壓縮,在GPU中也不需要對整個圖片解壓縮
  2. 解決了帶寬問題,從CPU傳到GPU的圖片是壓縮格式,因此傳輸的數據量會小很多,大大減輕了帶寬壓力
  3. 解決了包體大小的問題,打包到遊戲內的紋理是直接處理好的壓縮格式。
紋理壓縮的缺點:

因爲使用的是有損壓縮,3D表面細節會產生一定程度的損失,這屬於典型的效果換性能的妥協。


常見的幾種紋理壓縮格式:

ETC

Unity 支持4種格式的ETC紋理壓縮格式,主要應用於安卓設備的OpenGL es 上

  • RGB ETC 4 bit ,每像素佔用4bit大小,相比於24bit的RGB格式,壓縮比6:1,支持OpenGL es 2 版本
  • RGB ETC2 4 bit ,每像素佔用4bit大小,OpenGL es 3.0版本對ETC 格式的兼容。壓縮比以及功能和ETC1沒有差別,但是壓縮質量可能更高(不確定,需要測試)。
  • RGB +1bit Alpha ETC2 4bit ,每像素佔用4bit大小。支持1bit的透明通道,也就是隻支持鏤空圖,圖片只有透明和不透明部分,沒有中間的透明度。
  • RGBA ETC2 8bit ,每像素佔用8bit大小,相比於32bit的RGBA格式,壓縮比4:1。支持完全的透明通道

對於現代的移動端GPU,幾乎都支持OpenGL es 3.x,因此可以根據需要放心的使用所有的ETC格式。

DXT

Unity 支持兩種DXT紋理壓縮格式,RGB DXT1 和RGBA DXT5。主要應用於DirectX中

  • DXT1 不支持Alpha通道,壓縮後每像素佔用4bits。因此相比於16bitRGB格式的原始紋理(R6G5B6)壓縮比可以達到4:1,相比於24bit的RGB格式(R8G8B8),壓縮比可以達到6:1
  • DXT5 支持Alpha通道,壓縮後每像素佔用8bits。相比於32bitRGBA格式原始紋理(R8G8B8A8),壓縮比可以達到4:1。相比於16bitRGBA(R4G4B4A4)只能達到2:1的壓縮比。

對於PC平臺,大多數RGB格式圖片推薦DXT1壓縮格式,而對於RGBA格式圖片使用DXT5壓縮格式。

當目標平臺是DX11類型的硬件時(modern PC,PS 4, XboxOne)時,使用BC7可能是更好的選擇,因爲此時壓縮質量會更好。

該格式在Android平臺不管是選擇OpenGL es3.x或者Vulkan都不支持,實際佔用的內存比預期要高

BC6H

  • 對於使用HDR顏色通道的圖片(每一個通道需要16-bit)例如高精度天空盒,PBR流程中的環境貼圖等,推薦使用這種紋理壓縮格式進行壓縮,不支持Alpha通道
  • Unity GraphicsFormat的枚舉中支持兩種BC6H格式:
    • RGB_BC6H_UFloat : 5 指數位 11 尾數位
    • RGB_BC6H_SFloat : 1 符號位+ 5指數位+10 尾數位
  • 如果顯卡不支持該格式,在加載時會被解壓到RGBAHalf格式(64 bits 每像素)

不同平臺的推薦紋理壓縮格式

平臺 RGB 推薦格式 RGBA 推薦格式 HDR
PC/Console DXT1 DXT5 BC6H
Android ETC 4bit / ETC2 4bit ETC2 8bit
iOS

如果使用了錯誤的紋理壓縮格式會發生什麼?

如果使用了硬件不支持的紋理壓縮格式,Unity在運行時會對紋理進行解壓所到無壓縮格式,而這種無壓縮格式只是格式上是無壓縮的,但是因爲原始數據是有損壓縮,所以視覺上紋理精度和壓縮格式是一致的,因此單從視覺上很難察覺,而且佔用的內存是雙份紋理的開銷,而帶寬開銷是無壓縮格式的紋理大小所佔用的開銷。

例如:ETC格式在PC端Dx11的顯卡是不支持的,如果使用,開銷計算如下

  1. 1024*1024 ETC2 8bit的格式在PC端,會產生5M的內存開銷(1M壓縮的原圖+4M解壓出來的RGBA32bit圖)
  2. 1024*1024 ETC 4bit格式在PC端,會產生4.5M的內存開銷(0.5M壓縮的原圖+4M解壓出來的RGBA32bit圖)

而如果貼圖格式在目標設備上支持,則不會發生內存中的解壓縮操作,壓縮過的圖大小是多少,在內存中的設備上大小就是多少:

  1. 1024*1024 ETC2 8bit的格式在Android端,會產生1M的內存開銷(1M壓縮的原圖)
  2. 1024*1024 ETC 4bit格式在Android端,會產生0.5M的內存開銷(0.5M壓縮的原圖)

對於移動平臺遊戲,如果勾選了Unity中的 貼圖設置中的Override for Android ,一定要注意,除非你特別清楚你的貼圖在目標設備上的兼容情況,否則不要使用ETC格式以外的紋理壓縮格式,否則造成的額外內存開銷是巨大的。

iOS這裏因爲沒有在真機測試,因此沒有做說明,將在另一篇文章單獨討論iOS設備上的紋理壓縮格式。

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