mapbox地图上显示3D模型

mapbox是目前比较常用的地图框架,我们使用它来加载地图制作地图时,不免有加载3D模型的情况,mapbox官方提供了两种方式来渲染3D模型。

1、使用threebox

1、引入threebox.cssthreebox.min.js
2、初始化threebox
3、将模型放在指定位置

html 复制代码
  <!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>

  <script src='https://npmcdn.com/@turf/turf/turf.min.js'></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/v3.0.1/mapbox-gl.css" rel="stylesheet">
  <script src="https://api.mapbox.com/mapbox-gl-js/v3.0.1/mapbox-gl.js"></script>
  <script src="https://cdn.jsdelivr.net/gh/jscastro76/threebox@v.2.2.2/dist/threebox.min.js"></script>

  <style>
    body {
      margin: 0;
      padding: 0;
    }

    #map {
      position: absolute;
      top: 0;
      bottom: 0;
      width: 100%;
    }
  </style>
</head>

<body>

  <div id="map"></div>

  <script>
    mapboxgl.accessToken = 'pk.eyJ1IjoiYXl1MjM4OSIsImEiOiJjbGU2dTJtc2IwOHplM3JtcnljODZqNWszIn0.iszjybKhsIp0AYyfu8sQmg'
    const map = new mapboxgl.Map({
      container: 'map',
      // You can add layers to the predetermined slots within the Standard style basemap.
      style: 'mapbox://styles/mapbox/streets-v11', // mapbox官方给出了几种底图可以选择
      center: [104.6826114635814, 28.82097174542686], // 默认地图中心位置 宜宾学院
      zoom: 17 // 地图缩放级别
    })

    ...

    // 初始化threebox
    const tb = (window.tb = new Threebox(
      map,
      map.getCanvas().getContext('webgl'),
      {
        defaultLights: true
      }
    ))

    map.on('load', () => {

      ...

      // 在[104.6829214635714, 28.82097174542686]放置3D模型
      map.addLayer({
        id: 'custom-threebox-model',
        type: 'custom',
        renderingMode: '3d',
        onAdd: function() {
          const scale = 0.2
          const options = {
            obj: 'http://127.0.0.1:5501/mapbox-test/sofa3d/scene.gltf',
            type: 'gltf',
            scale: {x: scale, y: scale, z: 0.2},
            units: 'meters',
            rotation: {x: 90, y: -90, z: 0}
          }

          tb.loadObj(options, (model) => {
            model.setCoords([104.6829214635714, 28.82097174542686])
            model.setRotation({x: 0, y: 0, z: 241})
            tb.add(model)
          })
        },

        render: function() {
          tb.update()
        }
      })
    })
  </script>

</body>

</html>

使用threebox来加载3D模型确实方便简洁,但是要注意性能上的问题,个人猜测是封装框架的时候没有考虑到,测试的时候,一旦加载的模型过多,那么使用map.setLayoutProperty('custom-threebox-model6', 'visibility', 'none') 来隐藏模型,会没有效果,针对此类情况可以直接使用three.js框架。

2、直接使用three.js

three.js这个框架就不多做介绍了,非常有名的3D模型框架。
1、引入three.min.jsGLTFLoader.js
2、初始化three.js
3、将模型放在指定位置

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>

  <script src='https://npmcdn.com/@turf/turf/turf.min.js'></script>
  <link href="https://api.mapbox.com/mapbox-gl-js/v3.0.1/mapbox-gl.css" rel="stylesheet">
  <script src="https://api.mapbox.com/mapbox-gl-js/v3.0.1/mapbox-gl.js"></script>
  <script src="https://unpkg.com/three@0.126.0/build/three.min.js"></script>
  <script src="https://unpkg.com/three@0.126.0/examples/js/loaders/GLTFLoader.js"></script>

  <style>
    body {
      margin: 0;
      padding: 0;
    }

    #map {
      position: absolute;
      top: 0;
      bottom: 0;
      width: 100%;
    }
  </style>
</head>

<body>
  <div id="map"></div>

  <script>
    mapboxgl.accessToken = 'pk.eyJ1IjoiYXl1MjM4OSIsImEiOiJjbGU2dTJtc2IwOHplM3JtcnljODZqNWszIn0.iszjybKhsIp0AYyfu8sQmg'
    const map = new mapboxgl.Map({
      container: 'map',
      // You can add layers to the predetermined slots within the Standard style basemap.
      style: 'mapbox://styles/mapbox/streets-v11', // mapbox官方给出了几种底图可以选择
      center: [104.6826114635814, 28.82097174542686], // 默认地图中心位置 宜宾学院
      zoom: 17 // 地图缩放级别
    })

    ...

    // 需要放在哪个位置
    const modelOrigin = [104.6855214635714, 28.82097174542686]
    const modelAltitude = 0
    const modelRotate = [Math.PI / 2, 0, 0]

    const modelAsMercatorCoordinate = mapboxgl.MercatorCoordinate.fromLngLat(
      modelOrigin,
      modelAltitude
    )

    // transformation parameters to position, rotate and scale the 3D model onto the map
    const modelTransform = {
      translateX: modelAsMercatorCoordinate.x,
      translateY: modelAsMercatorCoordinate.y,
      translateZ: modelAsMercatorCoordinate.z,
      rotateX: modelRotate[0],
      rotateY: modelRotate[1],
      rotateZ: modelRotate[2],
      /* Since the 3D model is in real world meters, a scale transform needs to be
      * applied since the CustomLayerInterface expects units in MercatorCoordinates.
      */
      scale: modelAsMercatorCoordinate.meterInMercatorCoordinateUnits()
    }

    const THREE = window.THREE

    const customLayer = {
      id: '3d-model',
      type: 'custom',
      renderingMode: '3d',
      onAdd: function(map, gl) {
        this.camera = new THREE.Camera()
        this.scene = new THREE.Scene()

        const directionalLight = new THREE.DirectionalLight(0xffffff)
        directionalLight.position.set(0, -70, 100).normalize()
        this.scene.add(directionalLight)

        const directionalLight2 = new THREE.DirectionalLight(0xffffff)
        directionalLight2.position.set(0, 70, 100).normalize()
        this.scene.add(directionalLight2)

        const loader = new THREE.GLTFLoader()
        loader.load(
          'http://127.0.0.1:5501/mapbox-test/sofa3d/scene.gltf',
          (gltf) => {
            this.scene.add(gltf.scene)
          }
        )
        this.map = map

        this.renderer = new THREE.WebGLRenderer({
          canvas: map.getCanvas(),
          context: gl,
          antialias: true
        })

        this.renderer.autoClear = false
      },
      render: function(gl, matrix) {
        const rotationX = new THREE.Matrix4().makeRotationAxis(
          new THREE.Vector3(1, 0, 0),
          modelTransform.rotateX
        )
        const rotationY = new THREE.Matrix4().makeRotationAxis(
          new THREE.Vector3(0, 1, 0),
          modelTransform.rotateY
        )
        const rotationZ = new THREE.Matrix4().makeRotationAxis(
          new THREE.Vector3(0, 0, 1),
          modelTransform.rotateZ
        )

        const m = new THREE.Matrix4().fromArray(matrix)
        const l = new THREE.Matrix4()
          .makeTranslation(
            modelTransform.translateX,
            modelTransform.translateY,
            modelTransform.translateZ
          )
          .scale(
            new THREE.Vector3(
              modelTransform.scale,
              -modelTransform.scale,
              modelTransform.scale
            )
          )
          .multiply(rotationX)
          .multiply(rotationY)
          .multiply(rotationZ)

        this.camera.projectionMatrix = m.multiply(l)
        this.renderer.resetState()
        this.renderer.render(this.scene, this.camera)
        this.map.triggerRepaint()
      }
    }

    map.on('load', () => {
      // 先向mapbox加入源数据(geojson数据格式  urban-areas为ID,唯一标记该源数据)

      ...

      map.addLayer(customLayer);
    })

  </script>

</body>

</html>

综上,使用封装好的threebox来显示3D模型要简单很多,但是如果要自定义很多内容还是直接用three.js更好,不过用的时候,最好去深入了解一下three.js这个框架如何用的。

相关推荐
栈老师不回家19 分钟前
Vue 计算属性和监听器
前端·javascript·vue.js
前端啊龙24 分钟前
用vue3封装丶高仿element-plus里面的日期联级选择器,日期选择器
前端·javascript·vue.js
一颗松鼠28 分钟前
JavaScript 闭包是什么?简单到看完就理解!
开发语言·前端·javascript·ecmascript
小远yyds1 小时前
前端Web用户 token 持久化
开发语言·前端·javascript·vue.js
吕彬-前端2 小时前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱2 小时前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai2 小时前
uniapp
前端·javascript·vue.js·uni-app
bysking3 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
王哲晓3 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
fg_4113 小时前
无网络安装ionic和运行
前端·npm