共计 1893 个字符,预计需要花费 5 分钟才能阅读完成。
背景痛点:为什么需要 3D 模型压缩?
最近在做一个 Web 端的 3D 展厅项目,发现用户上传的家具模型平均大小竟有 15MB!用 Chrome DevTools 的 Network 面板抓包时看到:

- 一个未压缩的 GLB 格式沙发模型需要加载 12 秒(4G 网络环境下)
- 移动端用户首屏等待时间超过 20 秒,跳出率高达 63%
- 模型顶点数据占整个传输量的 82%(通过右键菜单选择 ”Show overview” 可见)
技术选型:为什么选择 Draco?
对比了三种主流方案:
- Draco(Google 开源)
- 压缩率:70%-90%(实测 GLB 从 18MB→2.1MB)
- 解码速度:需额外 1 - 3 秒解码时间
-
兼容性:通过 WebAssembly 支持所有现代浏览器
-
Meshopt(github.com/zeux/meshoptimizer)
- 压缩率:约 50%
- 优势:解码零开销
-
缺点:需要自定义渲染管线
-
传统 zip
- 只减少传输体积
- 内存中仍需展开原始数据
最终选择 Draco 的核心依据:
– Three.js 原生支持(通过 DRACOLoader)
– 对顶点属性(Vertex Attributes)有专门优化
– 支持有损 / 无损两种模式
实现方案:从压缩到渲染的全流程
Node.js 压缩流水线搭建
先安装关键工具链:
npm install gltf-pipeline draco3d
创建 compress.js 处理流水线:
const gltfPipeline = require('gltf-pipeline');
const fsExtra = require('fs-extra');
async function compressGLB(inputPath, outputPath) {const glb = await fsExtra.readFile(inputPath);
const options = {
dracoOptions: {
compressionLevel: 7, // 1-10,越高压缩率越大
quantizePositionBits: 14 // 顶点坐标精度
}
};
const results = await gltfPipeline.processGlb(glb, options);
await fsExtra.writeFile(outputPath, results.glb);
}
Three.js 前端加载优化
关键步骤:
-
预加载 WASM 解码器:
import {DRACOLoader} from 'three/examples/jsm/loaders/DRACOLoader'; const dracoLoader = new DRACOLoader(); dracoLoader.setDecoderPath('/draco/'); // WASM 文件目录 dracoLoader.preload(); -
使用 WebWorker 避免界面卡顿:
const loader = new GLTFLoader(); loader.setDRACOLoader(dracoLoader); // 在 Worker 中解码 const worker = new Worker('decodeWorker.js'); worker.postMessage({blob: modelBlob});
性能优化实战
Lighthouse 前后对比
| 指标 | 压缩前 | 压缩后 |
|---|---|---|
| 首屏加载 | 14.2s | 3.8s |
| 总阻塞时间 | 1.1s | 300ms |
| 传输体积 | 18MB | 2.1MB |
显存管理技巧
常见内存泄漏场景:
// 错误示范:直接替换模型不释放旧资源
function loadNewModel() {scene.add(newModel); // 旧 model 仍在显存
}
// 正确做法
function disposeModel(model) {
model.traverse(obj => {if (obj.isMesh) {obj.geometry.dispose(); // 释放几何体
obj.material.dispose(); // 释放材质}
});
}
避坑指南
iOS 兼容性问题
现象:Safari 上模型显示破碎
解决方案:
- 在 Draco 压缩时关闭
quantizeNormalBits - 检查 WASM 的 MIME 类型必须为
application/wasm - 添加 WebGL1 回退方案
避免重复压缩
检测是否已压缩:
function isDracoCompressed(gltf) {return gltf.extensionsUsed?.includes('KHR_draco_mesh_compression');
}
开放问题
在实际项目中发现:当使用 LOD(Level of Detail)分级时,高精度等级的压缩质量损失会导致明显锯齿。大家有什么平衡压缩精度与 LOD 分级的经验?欢迎评论区讨论!
(全文完)
正文完
发表至: 未分类
近三天内
