启用MapBox Terrain后Threebox 3D模型缩放时海拔异常问题
MapBox地形启用后Threebox 3D模型缩放异常的解决方案
问题描述
启用map.setTerrain加载MapBox DEM(数字高程模型)后,通过Threebox 2.2.2添加的DAE格式3D模型,在缩放地图时会出现海拔偏移,部分场景下模型会直接消失;但关闭地形功能时,模型显示完全正常。
原因分析
MapBox启用地形后,地图的地面海拔基准会随缩放层级动态变化(不同层级的DEM瓦片精度不同),而原代码中模型的z轴坐标是固定值,没有同步地形的实时海拔,导致模型与地面的相对位置随缩放出现偏差,甚至被地形遮挡或移出视野。
解决方案
1. 同步地形海拔计算模型位置
不要直接设置固定的z值,而是基于当前坐标的地形海拔加上模型的相对偏移量来定位,确保模型始终贴合地面或保持固定相对高度。
2. 监听缩放事件实时更新模型坐标
当地图缩放完成后,重新查询地形海拔并更新模型位置,避免因DEM瓦片精度变化导致的偏移。
3. 调整初始化顺序
确保Threebox和模型加载在地形资源初始化完成后执行,避免地形未就绪时的坐标计算错误。
修改后的完整代码
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>example</title> <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script> <script src="https://cdn.jsdelivr.net/gh/jscastro76/threebox@v.2.2.2/dist/threebox.min.js" type="text/javascript"></script> <link href="https://cdn.jsdelivr.net/gh/jscastro76/threebox@v.2.2.2/dist/threebox.css" rel="stylesheet"> <link href="https://api.mapbox.com/mapbox-gl-js/v2.13.0/mapbox-gl.css" rel="stylesheet"> <script src="https://api.mapbox.com/mapbox-gl-js/v2.13.0/mapbox-gl.js"></script> </head> <body> <div id="map"></div> <script> mapboxgl.accessToken = 'mapbox_key'; const map = new mapboxgl.Map({ container: 'map', projection: 'mercator', zoom: 15, center: [-4.254249, 42.908446], pitch: 0, bearing: 0, style: 'mapbox://styles/mapbox/streets-v12', maxZoom: 22, antialias: true }); // 加载地形并初始化Threebox map.on('style.load', () => { map.addSource('mapbox-dem', { 'type': 'raster-dem', 'url': 'mapbox://mapbox.mapbox-terrain-dem-v1', 'tileSize': 512, maxZoom: 22 }); map.setTerrain({ 'source': 'mapbox-dem', 'exaggeration': 1 }); // 地形就绪后初始化Threebox const tb = (window.tb = new Threebox( map, map.getCanvas().getContext('webgl'), {defaultLights: true} )); // 添加3D模型 map.addLayer({ id: 'custom-threebox-model', type: 'custom', renderingMode: '3d', onAdd: function () { const scale = 0.025; const options = { obj: 'https://webdemo.gisonline.es/SSEE_PICAL.dae', type: 'dae', scale: { x: scale, y: scale, z: scale }, units: 'meters', rotation: { x: 0, y: 0, z: 0 } }; const modelCoords = [-4.260950, 42.906500]; const modelOffset = -4; // 模型相对于地形的高度偏移 tb.loadObj(options, (model) => { // 初始获取地形海拔并设置模型位置 const initialElevation = map.queryTerrainElevation(modelCoords); model.setCoords([...modelCoords, initialElevation + modelOffset]); model.setRotation({ x: 0, y: 0, z: 180 }); tb.add(model); // 监听缩放事件,实时更新模型海拔 map.on('zoomend', () => { const updatedElevation = map.queryTerrainElevation(modelCoords); model.setCoords([...modelCoords, updatedElevation + modelOffset]); }); }); }, render: function () { tb.update(); } }); }); </script> </body> </html>
关键说明
- 使用
map.queryTerrainElevation()获取指定坐标的实时地形海拔,确保模型位置与地面基准同步 - 通过
zoomend事件监听缩放完成,避免缩放过程中频繁更新导致性能问题 - 将Threebox初始化和模型加载逻辑放在
style.load回调内,保证地形资源加载完成后再处理模型
内容的提问来源于stack exchange,提问作者Michiel Zevenbergen
相关产品推荐
相关产品推荐

