Meshopt 压缩深度解析,为什么它比 Draco 更快

从原理到实战,搞懂 Meshopt 和 Draco 的本质区别


前言

做 Web3D 的同学应该都知道 Draco 压缩,它是 Google 开发的 3D 几何数据压缩算法,Three.js、Babylon.js、model-viewer 都支持。

但最近 Meshopt 越来越火,官方宣称解码速度比 Draco 快 5-10 倍。这到底是不是吹牛?今天从原理层面分析一下。


先说结论

对比项 Draco Meshopt
压缩率 高(70-90%) 更高(75-95%)
解码速度 快 快 5-10 倍
编码速度 慢 快 3-5 倍
内存占用 中等 低
框架支持 广泛 Three.js r136+

结论:新项目优先用 Meshopt,老项目或需要兼容 model-viewer/Babylon.js 的用 Draco。


一、Draco 的原理

Draco 使用的是预测 + 熵编码的方式:

1.1 预测编码

对于网格中的每个顶点,Draco 会预测它的位置:

scss 复制代码
预测值 = f(相邻顶点)
残差 = 实际值 - 预测值

由于相邻顶点通常很接近,残差值会很小,存储残差比存储原始值更省空间。

1.2 熵编码

对残差值进行熵编码(类似 zip 压缩):

  • 出现频率高的值用短编码
  • 出现频率低的值用长编码

1.3 解码过程

解码时需要:

  1. 反向熵编码
  2. 重建预测
  3. 计算原始值

这个过程需要遍历整个网格拓扑,所以解码速度受限。


二、Meshopt 的原理

Meshopt(基于 gltfpack/meshoptimizer)采用的是量化 + 简单编码的方式:

2.1 量化

将浮点数转换为整数:

javascript 复制代码
// 原始值:0.123456789
// 量化为 16 位整数:8053(假设范围 [-1, 1])
quantized = Math.round((value - min) / (max - min) * 65535)

2.2 Delta 编码

存储相邻顶点的差值:

javascript 复制代码
// 顶点序列:[100, 102, 105, 103]
// Delta 编码:[100, 2, 3, -2]

2.3 简单变长编码

使用简单的变长整数编码(varint):

javascript 复制代码
// 小值用 1 字节,大值用更多字节
// 0-127: 1 字节
// 128-16383: 2 字节
// ...

2.4 解码过程

解码时只需要:

  1. 读取 varint
  2. 累加 delta
  3. 反量化

不需要遍历网格拓扑,所以解码速度极快。


三、为什么 Meshopt 更快

3.1 数据局部性

Meshopt 的数据是顺序存储的:

erlang 复制代码
顶点0.x, 顶点0.y, 顶点0.z
顶点1.x, 顶点1.y, 顶点1.z
顶点2.x, 顶点2.y, 顶点2.z
...

CPU 缓存友好,顺序读取效率高。

Draco 的数据是按拓扑存储的:

erlang 复制代码
面0的顶点、面1的顶点、面2的顶点...

需要随机访问,缓存命中率低。

3.2 算法复杂度

操作 Draco Meshopt
解码复杂度 O(n log n) O(n)
内存访问 随机 顺序
分支预测 难预测 易预测

3.3 实测数据

javascript 复制代码
// 测试代码
const decoder = new MeshoptDecoder()

console.time('Meshopt')
decoder.decodeVertexBuffer(target, vertexCount, vertexSize, source)
console.timeEnd('Meshopt')
// 约 2-5ms

const dracoLoader = new DRACOLoader()

console.time('Draco')
dracoLoader.decodeDracoFile(buffer)
console.timeEnd('Draco')
// 约 15-30ms

四、实际项目中的选择

4.1 选 Meshopt 的场景

  • Three.js 项目(r136+)
  • 追求极致加载性能
  • 移动端 Web
  • 需要快速解码的交互场景

4.2 选 Draco 的场景

  • 需要兼容 model-viewer
  • Babylon.js 项目
  • 老项目升级成本高
  • 需要最广泛的兼容性

4.3 两者都支持

使用 Zipoly(zipoly.netlify.app)可以:

  1. 选择 Meshopt 引擎
  2. 如果 Meshopt 不可用,自动降级到 Draco
  3. 一键压缩,无需手动配置

五、Three.js 中使用 Meshopt

5.1 基础配置

javascript 复制代码
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js'

const loader = new GLTFLoader()
loader.setMeshoptDecoder(MeshoptDecoder)

loader.load('/model.glb', (gltf) => {
  scene.add(gltf.scene)
})

5.2 配合 KTX2 纹理

javascript 复制代码
import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'

const ktx2Loader = new KTX2Loader()
ktx2Loader.setTranscoderPath('/basis/')
ktx2Loader.detectSupport(renderer)

const loader = new GLTFLoader()
loader.setMeshoptDecoder(MeshoptDecoder)
loader.setKTX2Loader(ktx2Loader)

loader.load('/model.glb', (gltf) => {
  scene.add(gltf.scene)
})

5.3 压缩命令

使用 gltfpack 命令行工具:

bash 复制代码
# 安装
npm install -g gltfpack

# 压缩
gltfpack -i input.glb -o output.glb -cc

# 参数说明
# -cc: 压缩顶点和索引
# -tc: 压缩纹理(KTX2)
# -si 0.5: 简化到 50%

六、Zipoly 一键压缩

6.1 操作步骤

  1. 下载 Zipoly:zipoly.netlify.app
  2. 导入 GLB 文件
  3. 选择"Web 3D 场景"预设(默认 Meshopt)
  4. 点击"开始优化"

6.2 压缩效果

模型 原始大小 Draco Meshopt
产品模型 45MB 8.2MB 6.8MB
角色模型 32MB 5.1MB 4.2MB
场景模型 120MB 22MB 18MB

七、性能对比测试

7.1 测试环境

  • 浏览器:Chrome 120
  • 设备:MacBook Pro M1
  • Three.js:r160

7.2 测试结果

指标 Draco Meshopt 提升
解码时间 28ms 4ms 7x
内存占用 45MB 32MB 29%
首帧时间 120ms 85ms 29%

总结

Meshopt 在解码速度和压缩率上都优于 Draco,是 Web3D 项目的首选压缩方案。

推荐工作流:

  1. 使用 Zipoly 压缩模型(默认 Meshopt)
  2. Three.js 中配置 MeshoptDecoder
  3. 配合 KTX2 纹理压缩,实现最佳性能

官网:zipoly.netlify.app 在线版:zipoly-web.netlify.app

相关推荐
总要学点什么1 小时前
第105篇:三维特效-流动图片墙体效果——让箭头沿墙长滚,它俩竟是同一份代码
开发语言·javascript·ecmascript·gis·webgl·材质·cesium
Agent世界3 小时前
用 CDP 做发布自动化:四道机器闸门与产物验收
前端·javascript
FungLeo3 小时前
成为全栈·Vue 管理后台篇·编辑器图片上传:从文件选择到文章附件关联
前端·vue.js·编辑器·markdown·文件上传·formdata·附件管理
总要学点什么3 小时前
第104篇:三维特效-流动墙体效果——让贴图沿墙流动,它却只肯走一条对角线
开发语言·javascript·ecmascript·gis·webgl·材质·cesium
西伯利亚小火龙3 小时前
我以为是流体解算,结果只是一张贴图:复刻 Lusion 的屏幕流体特效
webgl·three.js
某哈压力大3 小时前
SSH 突然 Connection reset?不是密钥坏了,是 OpenSSL 把 sshd 干趴了
前端·node.js·命令行
程序员大阳3 小时前
副队长ES6+教程(3)--解构赋值
前端·javascript·es6·赋值·解构
神膘护体小月半4 小时前
css - 元素背景图由 hover 触发,初次加载时闪动的解决办法
前端·javascript·css
程序员大阳4 小时前
副队长ES6+教程(2)--let 与 const
前端·javascript·es6·const·let·var
deli0074 小时前
写满 200GB 后 SSD 为什么掉到 95MB/s?SLC 缓存模型实测
前端