如何让Mapbox/Threebox中的3D模型自动贴合DEM图层?
自动让Threebox模型贴合Mapbox DEM地形的方法
解决思路
要让模型自动适配地形高度,核心就是用Mapbox的API拿到指定坐标的地形海拔,再把这个高度赋值给模型的Z轴,同时还要考虑模型自身的锚点偏移(避免模型埋地或悬空)。
具体实现
1. 加载模型时自动适配地形
修改模型加载的回调逻辑,先查询地形高度,再设置模型坐标:
threebox.current.loadObj(options, async (model) => { const lnglat = [23.72602949741895, 37.972941447007116]; // 调用Mapbox API获取当前坐标的地形高度(已考虑地形 exaggeration) const terrainHeight = await map.current.queryTerrainElevation(lnglat); // 处理模型锚点:如果模型原点不在底部,计算偏移量 model.object3D.geometry.computeBoundingBox(); const bbox = model.object3D.geometry.boundingBox; const modelHalfHeight = (bbox.max.z - bbox.min.z) / 2; // 假设原点在模型中心,偏移一半高度 // 设置模型坐标:地形高度 + 锚点偏移 model.setCoords([...lnglat, terrainHeight + modelHalfHeight]); threebox.current.add(model); });
2. 移动模型时实时更新高度(比如路径上的车辆)
如果模型需要沿路径移动,每次更新位置时重新查询地形高度即可:
// 示例:更新模型到新坐标 async function moveModelTo(newLnglat) { const terrainHeight = await map.current.queryTerrainElevation(newLnglat); const modelHalfHeight = (model.object3D.geometry.boundingBox.max.z - model.object3D.geometry.boundingBox.min.z) / 2; model.setCoords([...newLnglat, terrainHeight + modelHalfHeight]); }
3. 关键注意点
- 锚点偏移必须调:不同的GLTF模型原点位置不一样,一定要计算模型自身的高度偏移,不然模型要么卡进地里要么飘在空中。如果你的模型原点本来就在底部,那偏移量设为0就行。
- 异步处理别忽略:
queryTerrainElevation是异步方法,必须用await或者.then()等拿到高度后再设置坐标,不然模型初始位置会出错。 - 性能优化(多模型/高频移动场景):如果有大量模型或者需要频繁更新位置,可以把已经查询过的坐标和高度存在一个对象里做缓存,重复坐标直接用缓存值,减少API调用次数。
完整修改后的代码
// 初始化Threebox threebox.current = (window.tb = new Threebox( map.current, map.current.getCanvas().getContext('webgl'), { defaultLights: true, enableSelectingObjects:true, enableTooltips:true, realSunlight:true, sky:true } )); // 添加自定义模型图层 map.current.addLayer({ id: 'custom-threebox-model', type: 'custom', renderingMode: '3d', onAdd: async () => { const options = { obj: 'https://docs.mapbox.com/mapbox-gl-js/assets/metlife-building.gltf', type: 'gltf', scale: { x: 0.2, y: 0.2, z: 0.2 }, units: 'meters', rotation: { x: 90, y: -90, z: 0 } }; threebox.current.loadObj(options, async (model) => { const lnglat = [23.72602949741895, 37.972941447007116]; // 获取地形高度 const terrainHeight = await map.current.queryTerrainElevation(lnglat); // 计算模型锚点偏移 model.object3D.geometry.computeBoundingBox(); const bbox = model.object3D.geometry.boundingBox; const modelHalfHeight = (bbox.max.z - bbox.min.z) / 2; // 设置模型坐标 model.setCoords([...lnglat, terrainHeight + modelHalfHeight]); threebox.current.add(model); }); }, render: () => { threebox.current.update(); } }); // 添加DEM地形图层 map.current.addSource('mapbox-dem', { 'type': 'raster-dem', 'url': 'mapbox://mapbox-terrain-dem-v1', 'tileSize': 512, 'maxzoom': 18 }); map.current.setTerrain({ 'source': 'mapbox-dem', 'exaggeration': 1 });
内容的提问来源于stack exchange,提问作者slevin
相关产品推荐
相关产品推荐

