共计 2251 个字符,预计需要花费 6 分钟才能阅读完成。
Draco 压缩与 3D 模型优化
在 Web 端渲染大规模 3D 场景时,模型数据量往往成为性能瓶颈。Draco 作为开源几何压缩库,通过对网格、顶点和属性的高效编码,可实现 70%-90% 的体积缩减。这在 3D-Tiles 规范中尤为重要——当处理城市级 BIM 或倾斜摄影模型时,Draco 能显著降低:

- 网络传输时间(减少 HTTP 请求 payload)
- GPU 内存占用(解码后数据仍比原始格式紧凑)
- 解析开销(二进制流比 JSON 解析更快)
3D-Tiles 中的 Draco 集成规范
3D-Tiles 1.0 标准中,Draco 数据通过以下方式嵌入:
- glTF 扩展声明 :在
tileset.json的asset.extensionsUsed中需包含KHR_draco_mesh_compression - 缓冲区布局 :压缩后的几何数据存储在 glTF 的
buffers段,并通过bufferViews定义分段 - 属性映射 :
primitive.attributes中每个属性需指向 Draco 编码的bufferView索引
关键结构示例如下:
{
"meshes": [{
"primitives": [{
"attributes": {
"POSITION": 0,
"NORMAL": 1
},
"extensions": {
"KHR_draco_mesh_compression": {
"bufferView": 0,
"attributes": {
"POSITION": 0,
"NORMAL": 1
}
}
}
}]
}]
}
实战:3D-Tiles-Renderer 集成 Draco
1. 初始化解码环境
首先确保引入 Three.js 的 DracoLoader:
import {DRACOLoader} from 'three/examples/jsm/loaders/DRACOLoader';
const initDracoDecoder = () => {const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('/path/to/draco/decoder/'); // WASM 解码器路径
dracoLoader.setDecoderConfig({type: 'js'}); // 或 'wasm' 根据性能需求选择
return dracoLoader;
};
2. 改造 Tileset 加载流程
在 Cesium3DTileset 的加载器中注入解码器:
const loadCompressedTileset = async (url: string) => {const dracoLoader = initDracoDecoder();
const tileset = await Cesium3DTileset.fromUrl(url, {loadGLTF: (gltf: any) => {
// 关键:在 glTF 解析阶段拦截并注入 Draco 解码
const originalParse = gltf.parser.parse;
gltf.parser.parse = function (json: any, options: any) {
options.dracoLoader = dracoLoader;
return originalParse.call(this, json, options);
};
return gltf;
}
});
return tileset;
};
3. 内存管理策略
Draco 解码后的几何数据需主动释放:
// 在 tileset.unload 回调中释放资源
tileset.tileUnload.addEventListener((tile) => {tile.content.gltf?.scene?.traverse((obj) => {if (obj.geometry?.dispose) {obj.geometry.dispose();
}
});
});
性能实测数据
对比同一建筑模型(50 万三角面):
| 指标 | 未压缩 | Draco 压缩 | 优化率 |
|---|---|---|---|
| 文件大小 | 28MB | 3.2MB | 88.6%↓ |
| 下载时间(4G) | 4.7s | 0.9s | 80.9%↓ |
| 解码时间 | – | 1.2s | – |
| 总内存占用 | 146MB | 82MB | 43.8%↓ |
生产环境进阶方案
WebWorker 多线程解码
避免主线程阻塞:
// 在 DRACOLoader 中启用 Worker
dracoLoader.setWorkerLimit(4); // 根据 CPU 核心数调整
渐进式加载实现
分帧解码避免卡顿:
// 在 requestAnimationFrame 中分批处理
tileset.tileLoad.addEventListener((tile) => {if (tile.content.gltf) {scheduleInFrames(() => {tile.content.processDracoGeometry();
}, {framesPerChunk: 2});
}
});
解码异常降级方案
try {await loadCompressedTileset(url);
} catch (e) {console.warn('Draco 解码失败,回退到未压缩版本');
await loadFallbackTileset(uncompressedUrl);
}
延伸思考
- 如何结合 LOD 策略,对不同级别的 tile 动态切换压缩级别?
- Draco 压缩对动画骨骼数据的支持方案有哪些?
- 在 WebAssembly 解码模式下,如何预加载解码器进一步提升首屏速度?
通过本文方案,我们的智慧城市项目实现了整体加载性能提升 37%。建议在复杂场景中优先对高密度建筑 tile 启用 Draco,而对简单地形保留原始格式以节省解码开销。
正文完
发表至: 未分类
近两天内
