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

相关推荐
浏览器工程师2 小时前
AI Agent 接浏览器任务,先别让它一路点到底
前端·后端
雨季mo浅忆2 小时前
VSCode自动格式化三要素
前端
爱勇宝3 小时前
深扒 Anthropic 1680 位工程师简历:应届生几乎没机会,AI 公司最缺的不是博士
前端·后端·程序员
kyriewen4 小时前
同事每天催我 Code Review,我写了个脚本让 AI 替我 review PR——现在他反过来催 AI 了
前端·javascript·ai编程
user20585561518136 小时前
Windows 项目安装时报 `node-sass` 错误,如何快速处理
前端
LiaCode6 小时前
Redis 在生产项目的使用
前端·后端
LiaCode6 小时前
一天学完 redis 的爽翻版核心知识总结
前端·后端
大刚测试开发实战6 小时前
如何内网穿透访问本地私有化部署的TestHub
前端·后端·github
风骏时光牛马6 小时前
# Ruby基于Rails框架实现多角色权限管理与数据分页查询完整实战代码案例
前端