mapbox是目前比较常用的地图框架,我们使用它来加载地图制作地图时,不免有加载3D模型的情况,mapbox官方提供了两种方式来渲染3D模型。
1、使用threebox
1、引入
threebox.css
、threebox.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.js
、GLTFLoader.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这个框架如何用的。