最近作T級互動,須要使用到3D模型。相信你們和我同樣,在開始着手的時候,必定會有這麼些問題:javascript
讓咱們經過這篇文章,進行細緻的探索、調研與沉澱。java
glTF 全稱 Graphics Language Transmission Format
,是三維場景和模型的標準文件格式。android
glTF 核心是 JSON 文件,描述了 3D 場景的整個內容。它由場景結構自己的描述組成,其由定義場景圖的節點的層次提供。ios
場景中出現的 3D 對象是使用鏈接到節點的 meshes(網格)定義的。Materials(材料)定義對象的外觀。Animations(動畫)描述 3D 對象如何隨着時間的推移轉換 3D 對象,而且 Skins(蒙皮)定義了對物體的幾何形狀的方式基於骨架姿式變形。Cameras(相機)描述了渲染器的視圖配置。git
除此之外,它還包括了帶有二進制數據和圖像文件的連接,以下圖所示。github
從 blender 文件導出中能夠看出:
web
glTF 文件有兩種拓展形式,.gltf(JSON / ASCII)或.glb(二進制)。.gltf 文件多是自包含的,也可能引用外部二進制和紋理資源,而 .glb 文件則是徹底自包含的(但使用外部工具能夠將其緩衝區/紋理保存爲嵌入或單獨的文件,後面會提到)。算法
glTF 提供了兩個也能夠一塊兒使用的交付選項:性能優化
對於這些資源,因爲 base64 編碼,glTF 須要單獨的請求或額外的空間。Base64 編碼須要額外的處理來解碼並增長文件大小(編碼資源增長約 33%)。雖然 gzip 減輕了文件大小的增長,但解壓縮和解碼仍然會增長大量的加載時間。網絡
爲了解決這個問題,引入了一種容器格式 Binary glTF。在二進制 glTF 中,glTF 資產(JSON、.bin 和圖像)能夠存儲在二進制 blob 中,就是.glb 文件。
從圖中能夠看到,當非自包含型的時候,請求glTF文件時,會一同請求圖片文件。
那麼,咱們就能夠利用這個特性,就能夠實現一些性能優化,讓咱們往下繼續。
上文提到,glTF文件能夠拆分爲.gltf/.glb文件+二進制文件+紋理圖片,那麼,咱們就能夠將其拆分出來,並對紋理圖片進行單獨的壓縮,來進行性能的優化。
可使用gltf pipeLine
,其具備如下功能:
KHR_techniques_webgl
和KHR_blend
)在這裏,咱們是要使用「將緩衝區/紋理保存爲嵌入或單獨的文件」這個功能。
讓咱們來看看拆分出來的文件
再回顧一下,.glb文件是這麼引入外部單獨的紋理與二進制文件的
因此,只要將拆分出來的這幾個文件,放入同一個路徑中,而後像以前那樣引入就行了。
gltf-pipeline -i male.glb -o male-processed.glb -s
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader' const loader = new GLTFLoader() loader.load(MODEL_FILE_PATH, (gltf) => { // .... })
如上面介紹,glTF 文件包括.gltf/.glb 文件、.bin 文件以及紋理資源。glTF2.0 相關的插件主要有如下:
那麼咱們從中取一些來分析一下。
最常見的一種網格壓縮方式,採用開源的Draco算法,用於壓縮和解壓縮3D 網格和點雲,而且可能會改變網格中頂點的順序和數量。壓縮的使文件小得多,可是在客戶端設備上須要額外的解碼時間。
可使用gltf-pipeline
gltf 文件優化工具進行壓縮
gltf-pipeline -i male.glb -o male-processed.glb -d
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader' import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader' const loader = new GLTFLoader() // 建立解碼器實例 const dracoLoader = new DRACOLoader() // 設置解壓庫文件路徑 dracoLoader.setDecoderPath(DECODER_PATH) // 加載解碼器實例 loader.setDRACOLoader(dracoLoader) loader.load(MODEL_FILE_PATH, (gltf) => { // .... })
這個 glb 文件原大小爲 3.2M,draco 壓縮後爲 1.8M,約爲原文件的56%。
從上面的代碼中能夠看出,建立解碼器實例須要引入額外的庫來進行解碼,setDecoderPath
會自動請求 wasm 文件來進行解密操做。而這兩個 wasm 文件同時也增長了請求時間和請求數量,那麼加上這兩個文件,真實的壓縮率約爲62.5%。
因此,若是一個項目須要加載多個 glTF 文件,那麼能夠建立一個 DRACOLoader 實例並重復使用它。但若是項目只須要加載一個 glTF 文件,那麼使用 draco 算法是否具備「性價比」就值得考量了。
用 demo 進行一下性能對比:
可見 draco 算法首次加載和解密時間,要大於原文件。而在實際項目中,這個差距更加明顯,而且偶爾會出現解密堵塞的狀況,須要從新進入頁面才能恢復功能。
除此之外,還有一個很直觀的問題,模型畫質的損失是肉眼可觀的。
如圖,分別是在 iPhone 12 和小米 MIX2 中的樣子:
總而言之,若是要將 draco 壓縮算法運用到大規模項目中,須要結合實際項目進行如下對比:
頂點屬性一般使用FLOAT
類型存儲,將原始始浮點值轉換爲16位或8位存儲以適應統一的3D或2D網格,也就是咱們所說的quantization向量化,該插件主要就是將其向量化。
例如,靜態 PBR-ready 網格一般須要每一個頂點POSITION
(12 字節)、TEXCOORD
(8 字節)、NORMAL
(12 字節)和TANGENT
(16 字節),總共 48 字節。經過此擴展,能夠用於SHORT
存儲位置和紋理座標數據(分別爲 8 和 4 字節)以及BYTE
存儲法線和切線數據(各 4 字節),每一個頂點總共 20 字節。
可使用gltfpack
工具進行壓縮
gltfpack -i male.glb -o male-processed.glb
普普統統地用就行了,和不壓縮的沒什麼區別
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader' const loader = new GLTFLoader() loader.load(MODEL_FILE_PATH, (gltf) => { // .... })
原文件3.2M,壓縮後1.9M,爲原文件的59.3%,比原模型加載速度也快上很多。
放到實際項目中,沒有畫質損失和加載時間過長的問題。
此插件假定緩衝區視圖數據針對 GPU 效率進行了優化——使用量化並使用最佳數據順序進行 GPU 渲染——並在 bufferView 數據之上提供一個壓縮層。每一個 bufferView 都是獨立壓縮的,這容許加載器最大程度地將數據直接解壓縮到 GPU 存儲中。
除了優化壓縮率以外,壓縮格式還具備兩個特性——很是快速的解碼(使用 WebAssembly SIMD,解碼器在現代桌面硬件上以約 1 GB/秒的速度運行),以及與通用壓縮兼容的字節存儲。也就是說,不是儘量地減小編碼大小,而是以通用壓縮器能夠進一步壓縮它的方式構建比特流。
可使用gltfpack
工具進行壓縮
gltfpack -i male.glb -o male-processed.glb -cc
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader' import { MeshoptDecoder } from 'three/examples/jsm/libs/meshopt_decoder.module.js' const loader = new GLTFLoader() loader.setMeshoptDecoder(MeshoptDecoder) loader.load(MODEL_FILE_PATH, (gltf) => { // .... })
原文件3.2M,壓縮後1.1M,爲原文件的65.6%,首次加載時間比原模型快上很多。
放到實際項目中,沒有畫質損失和加載時間過長的問題。
爲了不上文提到的「draco」壓縮使得模型受損的狀況,找了幾臺iPhone、安卓的手機來進行了一下性能與兼容的測試,讓咱們看一下結果。
PS:公司網絡在不一樣時間段內網速不一樣(如上午和下午),可能會對數字產生小部分影響,但不影響文件優化橫向對比。
可見,對於小部分須要使用模型的,而且只須要加載一個模型的業務,採用KHR_mesh_quantization
或EXT_meshopt_compression
進行網格壓縮,再使用gltf-pipeline
進行模塊區分並對紋理圖片壓縮,是目前找到的較好的優化方案。
其實還有不少性能優化的插件,目前正在進行調試和調查,等後續迭代或有什麼新進展,會繼續更新:
網格優化的:
現 Three.js 的 GLTFLoader 還沒有支持,Babylon.js 的BABYLON.GLTF2.Loader.Extensions 支持
還有一些紋理優化的插件:
歡迎關注凹凸實驗室博客:aotu.io
或者關注凹凸實驗室公衆號(AOTULabs),不定時推送文章: