共计 2420 个字符,预计需要花费 7 分钟才能阅读完成。
背景与痛点
3DTiles 是 Cesium 团队为流式加载大规模 3D 地理数据设计的开放格式,而 GLB 是 glTF 的二进制版本,适合 Web 端高效渲染。两者结合时面临的核心问题:

- 坐标系统差异:3DTiles 通常采用 EPSG:4978(地心坐标系),而地图数据多为 EPSG:4326(WGS84)或 Web 墨卡托(EPSG:3857)
- 数据量问题:城市级 3DTiles 可能包含数千万三角面片,直接转换会导致 GLB 文件过大
- 属性保留:3DTiles 中的 BATCH_ID 等属性如何在 GLB 中保持
技术方案对比
Cesium 方案
优点:
– 原生支持 3DTiles 解析
– 内置坐标转换系统
缺点:
– 无法直接导出 GLB
– 需要额外处理纹理压缩
Three.js 方案
优点:
– 完整的 GLB 导出管线
– 灵活的着色器修改能力
缺点:
– 需要自行实现 3DTiles 解析
推荐采用Cesium+Three.js 混合方案:用 Cesium 解析原始数据,通过 Three.js 进行格式转换和优化。
核心实现
1. 坐标系统转换
关键步骤:
-
使用 Cesium 加载 3DTiles
const tileset = await Cesium.Cesium3DTileset.fromUrl('tileset.json'); -
获取模型矩阵并转换坐标系
const cartographic = Cesium.Cartographic.fromCartesian(position); const [longitude, latitude, height] = [Cesium.Math.toDegrees(cartographic.longitude), Cesium.Math.toDegrees(cartographic.latitude), cartographic.height ]; -
重建 Three.js 中的模型矩阵
const matrix = new THREE.Matrix4().makeTranslation(x, y, z);
2. 数据优化策略
-
LOD 处理:
function generateLODs(mesh, levels = 3) {const lod = new THREE.LOD(); for (let i = 0; i < levels; i++) {const simplified = simplifyModifier.modify(mesh, i * 0.2); lod.addLevel(simplified, i * 100); } return lod; } -
实例化渲染:
const instancedMesh = new THREE.InstancedMesh(geometry, material, count); instancedMesh.setMatrixAt(index, matrix);
3. 完整代码示例
async function convert3DTilesToGLB(tilesetUrl: string) {
// 1. 加载 3DTiles
const tileset = await Cesium.Cesium3DTileset.fromUrl(tilesetUrl);
// 2. 创建 Three.js 场景
const scene = new THREE.Scene();
// 3. 递归处理所有瓦片
tileset.tiles.forEach(tile => {
const content = tile.content;
if (content.gltf) {const modelMatrix = getTransformMatrix(tile);
const gltf = await loadGLTF(content.gltf);
// 应用坐标转换
gltf.scene.applyMatrix4(modelMatrix);
scene.add(gltf.scene);
}
});
// 4. 导出 GLB
const exporter = new GLTFExporter();
const glb = await exporter.parseAsync(scene, {
binary: true,
maxTextureSize: 2048
});
// 5. 保存文件
saveAs(new Blob([glb]), 'converted.glb');
}
性能优化
内存管理
-
使用
dispose()及时释放资源geometry.dispose(); texture.dispose(); -
实现分块加载
const loader = new Cesium3DTileset({ dynamicScreenSpaceError: true, maximumScreenSpaceError: 2 });
渲染优化
-
开启 Frustum Culling
const renderer = new THREE.WebGLRenderer({logarithmicDepthBuffer: true}); -
使用 Web Worker 处理数据
const worker = new Worker('converter.worker.js'); worker.postMessage({tileset});
避坑指南
常见问题
- 纹理丢失:检查相对路径转换,建议使用绝对 URL
- 坐标偏移:确保所有转换使用同一基准点
- 性能骤降:监控 draw calls,合理使用
mergeVertices
调试技巧
// 显示坐标系辅助
scene.add(new THREE.AxesHelper(100));
// 性能监测
const stats = new Stats();
document.body.appendChild(stats.dom);
总结与扩展
本文方案已成功应用于智慧城市项目的 BIM+GIS 融合场景。对于更复杂的需求,可以考虑:
- 使用
KHR_mesh_quantization扩展减少 GLB 体积 - 集成 Cesium Ion 实现云端数据预处理
思考题:
1. 如何处理 3DTiles 中的点云数据转换?
2. 在保持属性数据的前提下,如何优化 GLB 的解析效率?
完整项目示例见 GitHub 仓库(示例链接)。在实际项目中,建议根据数据特点调整 LOD 策略和实例化阈值。
正文完
发表至: 未分类
近一天内
