1. 圖片格式和紋理格式
圖片格式,如 jpg/png 等
紋理格式杆融,如 ETC1/ETC2/PVRTC/ASTC等
1.1 圖片
電腦硬盤上的圖片文件用來保存圖形或圖像的信息髓堪,包括大小竞思、顏色等。圖片的格式很多刮刑,但總體上可以分為點(diǎn)陣圖和矢量圖兩種喉祭。
- 點(diǎn)陣圖:也叫位圖,記錄畫面上每一個(gè)像素的顏色信息雷绢,當(dāng)對(duì)點(diǎn)陣圖進(jìn)行縮放時(shí)會(huì)失真
- 矢量圖:也叫向量圖泛烙,矢量圖不記錄畫面中每個(gè)像素的信息,而是記錄元素的形狀和顏色的算法翘紊,當(dāng)矢量圖被打開時(shí)蔽氨,查看軟件根據(jù)矢量圖中的信息進(jìn)行運(yùn)算,展示運(yùn)算的結(jié)果。無論顯示畫面是大還是小鹉究,畫面對(duì)應(yīng)的算法是不變的宇立,所以對(duì)畫面進(jìn)行倍數(shù)相當(dāng)大的縮放,其顯示效果不會(huì)失真
常見的位圖格式:
- bmp 無壓縮位圖格式
- png 使用 lz77 壓縮算法對(duì)位圖進(jìn)行了無損壓縮自赔,帶 alpha 通道
- jpg 有損壓縮格式妈嘹,不帶 alpha 通道
1.2 點(diǎn)陣圖文件大小因素
考慮無壓縮的 bmp 格式位圖,文件在硬盤上的大小和以下幾個(gè)因素有關(guān)
顏色的豐富度
假如一張位圖只有黑白兩種顏色绍妨,每個(gè)像素使用一位數(shù)字即可表示润脸,若使用一個(gè)字節(jié)(8位)來表示一個(gè)像素的顏色,則可以表示=256種顏色他去,最用的位圖毙驯,將像素顏色的RGBA分量都使用一個(gè)字節(jié)來表示,一個(gè)像素需要4個(gè)字節(jié)表示
像素?cái)?shù)量
像素?cái)?shù)量越多灾测,需要越大的空間來表示爆价,像素的數(shù)量由圖片的寬度和高度決定計(jì)算公式
常用的位圖(RGBA32)文件大小計(jì)算公式jpg 和 png 格式圖片的大小
jpg 和 png 因?yàn)獒槍?duì)圖片的像素?cái)?shù)據(jù)進(jìn)行了壓縮,節(jié)省了空間媳搪,但因?yàn)閴嚎s算法的原因允坚,壓縮比與圖片本身的顏色分布有關(guān),圖片的大小沒有固定的計(jì)算方法蛾号,一個(gè)大概的趨勢是:顏色的分布越有序,圖片文件越小涯雅,也就是相鄰像素的顏色相差越小鲜结,文件越小。關(guān)于 jpg 和 png 的壓縮算法活逆,可以參考PNG圖片壓縮原理解析和PNG原理以及jpeg壓縮算法
1.3 紋理
圖片文件被加載到內(nèi)存后精刷,被轉(zhuǎn)換為顯卡能夠識(shí)別的紋理提交到顯存中,供 GPU 進(jìn)行采樣蔗候,紋理采樣是根據(jù) uv 坐標(biāo)獲得對(duì)應(yīng)紋理中紋素顏色值的過程怒允。
不同的顯卡和圖形API對(duì)紋理格式的支持不一樣,RGBA32被顯卡和圖形API完全支持锈遥,DirectX特有的DXTx只有微軟的部分設(shè)備支持纫事,Android設(shè)備廣泛支持ETC系列,iOS則支持 PVRTC 系列所灸。
1.4 加載紋理的過程
CPU將圖片從硬盤加載到內(nèi)存中丽惶,進(jìn)行解碼得到 RGBA32 格式位圖,然后在 CPU 端進(jìn)行編碼爬立,得到GPU支持的紋理格式钾唬,提交到顯存供 GPU 采樣訪問。
這個(gè)過程中需要進(jìn)行解碼、重新編碼抡秆,非常消耗 CPU奕巍,為了減少 CPU 的消耗,因此可以把圖片預(yù)先轉(zhuǎn)換成某種紋理格式放到硬盤上儒士,CPU 加載后不需要解碼和重新編碼的止,直接提交給 GPU。大大節(jié)省了紋理從加載到使用的性能乍桂。
1.5 Unity 中的處理
在Unity中冲杀,任何圖片文件格式都存在一個(gè)導(dǎo)入過程,導(dǎo)入后的文件格式都是Texture2D睹酌,在Texture2D的導(dǎo)入設(shè)置選項(xiàng)中需要針對(duì)不同平臺(tái)設(shè)置紋理壓縮格式权谁,在構(gòu)建時(shí),會(huì)直接將轉(zhuǎn)換后的紋理格式構(gòu)建到安裝包或 assetbundle 中憋沿。
2. 問題
2.1 紋理為什么需要壓縮旺芽,為什么不直接使用 RGBA32 格式?
使用紋理壓縮格式的好處
- 減少外存大小辐啄,也就是在硬盤或安裝包的大小采章,使用RGBA32格式,一個(gè)像素的紋理占用4個(gè)字節(jié)壶辜,1024x1024的圖片將占用4MB的安裝包空間悯舟,使用壓縮格式的紋理可以顯著減少空間占用
- 減少內(nèi)存大小,紋理被加載到內(nèi)存時(shí)砸民,占用的內(nèi)存空間和外存一樣大抵怎,不需要解壓成RGBA32格式,GPU端也不需要解壓岭参,可以直接訪問
- 帶寬消耗反惕,從CPU傳遞給GPU的是壓縮數(shù)據(jù),數(shù)據(jù)量小演侯,在移動(dòng)平臺(tái)上姿染,GPU和CPU共享內(nèi)存,GPU芯片讀取內(nèi)存同樣消耗帶寬秒际,帶寬消耗過大通常是設(shè)備發(fā)熱的最大元兇
2.2 為什么不直接將 jpeg/png 作為紋理格式提交給 GPU
GPU 通常并行處理若干數(shù)據(jù)悬赏,無法預(yù)測紋理中紋素被訪問的先后順序,因此紋理格式必須支持GPU的隨機(jī)訪問:
隨機(jī)訪問:任意給定像素坐標(biāo)娄徊,能夠快速算出它在圖像數(shù)據(jù)中的地址舷嗡,從而取得圖像數(shù)據(jù)
幾乎所有的紋理壓縮算法都以塊為單位壓縮和存儲(chǔ)紋素,可以理解為嵌莉,每個(gè)紋素塊(通常為4x4)占用的字節(jié)數(shù)是確定的进萄,所以當(dāng)我們需要訪問某一個(gè)坐標(biāo)的紋素時(shí),可以明確得到該紋素所在塊的數(shù)據(jù),GPU紋理采樣單元針對(duì)該塊進(jìn)行解壓中鼠,讀取對(duì)應(yīng)紋素?cái)?shù)據(jù)即完成采樣可婶。
而jpeg/png這類壓縮算法考慮了圖片的整體數(shù)據(jù),像素?cái)?shù)據(jù)之間互相依賴援雇,無法針對(duì)其中某一塊進(jìn)行解壓矛渴,必須全部解壓才能正確訪問紋素,所以不適宜作為紋理格式惫搏。
png/jpeg 等壓縮方法通常用來優(yōu)化文件的存儲(chǔ)和傳輸
2.3 紋理壓縮有損嗎具温?
紋理壓縮算法幾乎都是有損壓縮,通常情況下有限度的信息損失換到的性能上的提升是值得的筐赔。
2.4 如果使用了 GPU 和圖形 API 不支持的紋理壓縮格式铣猩,會(huì)發(fā)生什么?
如果使用了硬件不支持的紋理壓縮格式茴丰,Unity在運(yùn)行時(shí)會(huì)對(duì)紋理進(jìn)行解壓所到無壓縮格式达皿,而這種無壓縮格式只是格式上是無壓縮的,但是因?yàn)樵紨?shù)據(jù)是有損壓縮贿肩,所以視覺上紋理精度和壓縮格式是一致的峦椰,因此單從視覺上很難察覺,而且占用的內(nèi)存是雙份紋理的開銷汰规,而帶寬開銷是無壓縮格式的紋理大小所占用的開銷汤功。
例如:ETC格式在PC端Dx11的顯卡是不支持的,如果使用溜哮,開銷計(jì)算如下
1024 x 1024 ETC2 8bit的格式在PC端冤竹,會(huì)產(chǎn)生5M的內(nèi)存開銷(1M壓縮的原圖+4M解壓出來的RGBA32bit圖)
1024 x 1024 ETC 4bit格式在PC端,會(huì)產(chǎn)生4.5M的內(nèi)存開銷(0.5M壓縮的原圖+4M解壓出來的RGBA32bit圖)
而如果貼圖格式在目標(biāo)設(shè)備上支持茬射,則不會(huì)發(fā)生內(nèi)存中的解壓縮操作,壓縮過的圖大小是多少冒签,在內(nèi)存中的設(shè)備上大小就是多少:
1024 x 1024 ETC2 8bit的格式在Android端在抛,會(huì)產(chǎn)生1M的內(nèi)存開銷(1M壓縮的原圖)
1024 x 1024 ETC 4bit格式在Android端,會(huì)產(chǎn)生0.5M的內(nèi)存開銷(0.5M壓縮的原圖)
2.5 紋理壓縮和解壓是速度重要嗎萧恕?
紋理壓縮是一次性的工作刚梭,例如 Unity 在導(dǎo)入資源時(shí)會(huì)有一段轉(zhuǎn)換時(shí)間,圖片被處理成對(duì)應(yīng)的紋理格式后存儲(chǔ)了下來票唆,此時(shí)紋理壓縮的速度慢一些是可以接受的
GPU 對(duì)紋理進(jìn)行采樣時(shí)朴读,通常只會(huì)解壓一個(gè)紋素塊,不會(huì)全部解壓走趋,這個(gè)過程本身就是很快的