最近作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),不定时推送文章: