You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在启用3D地形的react-map-gl实例中添加自定义3D物体?

在启用3D地形的react-map-gl中添加自定义3D物体的方案

方案一:Mapbox GL JS v2 自定义图层 + Three.js

react-map-gl本质是Mapbox GL JS的React封装,直接用Mapbox v2的自定义图层能力结合Three.js就能实现需求,完全支持3D地形:

  • 自定义图层需实现Mapbox的CustomLayerInterface接口,核心是复用Mapbox的WebGL上下文,避免重复创建渲染环境
  • 通过Mapbox的queryTerrainElevation方法获取指定经纬度的地形海拔,将经纬度坐标转换为Three.js世界坐标,让3D物体贴合地形
  • 在react-map-gl的<Map>组件中注册这个自定义图层

简单代码示例:

import { useRef, useEffect } from 'react';
import Map from 'react-map-gl';
import * as THREE from 'three';

class ThreeCustomLayer {
  constructor(id, map, options) {
    this.id = id;
    this.type = 'custom';
    this.renderingMode = '3d';
    this.map = map;
    this.options = options;
    this.scene = null;
    this.camera = null;
    this.renderer = null;
    this.mesh = null;
  }

  onAdd(map, gl) {
    this.scene = new THREE.Scene();
    this.camera = new THREE.PerspectiveCamera();
    this.renderer = new THREE.WebGLRenderer({
      canvas: gl.canvas,
      context: gl,
      antialias: true
    });
    this.renderer.autoClear = false;

    // 创建3D立方体
    const geometry = new THREE.BoxGeometry(50, 50, 50);
    const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
    this.mesh = new THREE.Mesh(geometry, material);

    // 获取地形海拔并设置物体位置(让立方体顶部和地形齐平)
    const { lng, lat } = this.options.position;
    const elevation = map.queryTerrainElevation([lng, lat]);
    const mercatorCoord = map.project([lng, lat]);
    this.mesh.position.set(mercatorCoord.x, mercatorCoord.y, elevation + 25);
    this.scene.add(this.mesh);
  }

  render(gl, matrix) {
    const cameraMatrix = new THREE.Matrix4().fromArray(matrix);
    this.camera.projectionMatrix = cameraMatrix;
    this.renderer.state.reset();
    this.renderer.render(this.scene, this.camera);
    this.map.triggerRepaint();
  }
}

function MapWith3DObject() {
  const mapRef = useRef(null);

  useEffect(() => {
    if (mapRef.current) {
      const map = mapRef.current.getMap();
      const customLayer = new ThreeCustomLayer('three-layer', map, {
        position: { lng: -122.4194, lat: 37.7749 } // 示例坐标:旧金山
      });
      map.addLayer(customLayer);

      return () => {
        map.removeLayer('three-layer');
      };
    }
  }, []);

  return (
    <Map
      ref={mapRef}
      initialViewState={{
        longitude: -122.4194,
        latitude: 37.7749,
        zoom: 14,
        pitch: 60,
        bearing: 0
      }}
      style={{ width: '100vw', height: '100vh' }}
      mapStyle="mapbox://styles/mapbox/outdoors-v12"
      mapboxAccessToken={process.env.REACT_APP_MAPBOX_TOKEN}
      terrain={{ source: 'mapbox-dem', exaggeration: 1.5 }}
    />
  );
}

export default MapWith3DObject;

方案二:使用deck.gl的3D图层

deck.gl和react-map-gl深度兼容,支持直接加载3D模型并贴合Mapbox v2地形,无需手动处理WebGL上下文:

  • 用ScenegraphLayer加载glTF/GLB格式的3D模型,自动适配地形高度
  • 通过elevationScale参数控制模型相对于地形的高度,getPosition指定模型的经纬度坐标

示例代码片段:

import { Map } from 'react-map-gl';
import { ScenegraphLayer } from '@deck.gl/mapbox';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

// 预加载3D模型
let model;
const loader = new GLTFLoader();
loader.load('/path/to/your/model.glb', (gltf) => {
  model = gltf.scene;
});

function MapWithDeckGL3DObject() {
  const layers = [
    model && new ScenegraphLayer({
      id: 'scenegraph-layer',
      data: [{ position: [-122.4194, 37.7749] }],
      scenegraph: model,
      getPosition: d => d.position,
      getScale: [10, 10, 10], // 缩放模型大小
      elevationScale: 1, // 让模型贴合地形
      pickable: true
    })
  ].filter(Boolean);

  return (
    <Map
      initialViewState={{
        longitude: -122.4194,
        latitude: 37.7749,
        zoom: 14,
        pitch: 60,
        bearing: 0
      }}
      style={{ width: '100vw', height: '100vh' }}
      mapStyle="mapbox://styles/mapbox/outdoors-v12"
      mapboxAccessToken={process.env.REACT_APP_MAPBOX_TOKEN}
      terrain={{ source: 'mapbox-dem', exaggeration: 1.5 }}
      layers={layers}
    />
  );
}

export default MapWithDeckGL3DObject;

关键注意事项

  • 坐标转换:Mapbox使用墨卡托投影,需将经纬度转换为Mapbox的屏幕/世界坐标后再映射到Three.js空间
  • 地形高度更新:当地形加载完成或相机移动时,需重新查询地形海拔并更新3D物体的Z轴位置
  • 性能优化:复用WebGL上下文、合并3D模型几何体、避免在每一帧重复创建对象

内容的提问来源于stack exchange,提问作者bitTorin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 22:20:47