使用three.js添加3D模型
使用自定义样式图层和three.js向地图添加3D模型;
<!DOCTYPE html>
<html lang="en">
<head>
<title>使用three.js添加3D模型</title>
<meta property="og:description" content="使用自定义样式图层和three.js向地图添加3D模型" />
<meta charset='utf-8'>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel='stylesheet' href='https://unpkg.com/maplibre-gl@5.5.0/dist/maplibre-gl.css' />
<script src='https://unpkg.com/maplibre-gl@5.5.0/dist/maplibre-gl.js'></script>
<style>
body { margin: 0; padding: 0; }
html, body, #map { height: 100%; }
</style>
</head>
<body>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.169.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.169.0/examples/jsm/"
}
}
</script>
<div id="map"></div>
<script type="module">
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const map = new maplibregl.Map({
container: 'map',
style:
'https://api.maptiler.com/maps/basic/style.json?key=get_your_own_OpIi9ZULNHzrESv6T2vL',
zoom: 18,
center: [148.9819, -35.3981],
pitch: 60,
canvasContextAttributes: {antialias: true} // 创建带有MSAA抗锯齿的gl上下文,使自定义图层抗锯齿
});
// 参数确保模型在地图上正确地理参考
const modelOrigin = [148.9819, -35.39847];
const modelAltitude = 0;
const modelRotate = [Math.PI / 2, 0, 0];
const modelAsMercatorCoordinate = maplibregl.MercatorCoordinate.fromLngLat(
modelOrigin,
modelAltitude
);
// 用于定位、旋转和缩放3D模型到地图上的转换参数
const modelTransform = {
translateX: modelAsMercatorCoordinate.x,
translateY: modelAsMercatorCoordinate.y,
translateZ: modelAsMercatorCoordinate.z,
rotateX: modelRotate[0],
rotateY: modelRotate[1],
rotateZ: modelRotate[2],
/* 由于我们的3D模型是真实世界的米单位,需要应用缩放变换,
* 因为CustomLayerInterface期望单位是墨卡托坐标。
*/
scale: modelAsMercatorCoordinate.meterInMercatorCoordinateUnits()
};
// 根据CustomLayerInterface为3D模型配置自定义图层
const customLayer = {
id: '3d-model',
type: 'custom',
renderingMode: '3d',
onAdd (map, gl) {
this.camera = new THREE.Camera();
this.scene = new THREE.Scene();
// 创建两个three.js灯光来照亮模型
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);
// 使用three.js GLTF加载器将3D模型添加到three.js场景
const loader = new GLTFLoader();
loader.load(
'https://maplibre.org/maplibre-gl-js/docs/assets/34M_17/34M_17.gltf',
(gltf) => {
this.scene.add(gltf.scene);
}
);
this.map = map;
// 使用MapLibre GL JS地图画布作为three.js的渲染目标
this.renderer = new THREE.WebGLRenderer({
canvas: map.getCanvas(),
context: gl,
antialias: true
});
this.renderer.autoClear = false;
},
render (gl, args) {
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(args.defaultProjectionData.mainMatrix);
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);
// 或者,您可以使用以下API获取正确的模型矩阵。
// 无论当前投影如何,它都会起作用。
// 另请参见示例 "globe-3d-model.html"。
//
// const modelMatrix = args.getMatrixForModel(modelOrigin, modelAltitude);
// const m = new THREE.Matrix4().fromArray(matrix);
// const l = new THREE.Matrix4().fromArray(modelMatrix);
this.camera.projectionMatrix = m.multiply(l);
this.renderer.resetState();
this.renderer.render(this.scene, this.camera);
this.map.triggerRepaint();
}
};
map.on('style.load', () => {
map.addLayer(customLayer);
});
</script>
</body>
</html>