使用babylon.js添加3D模型
使用自定义样式图层和babylon.js向地图添加3D模型;
<!DOCTYPE html>
<html lang="en">
<head>
<title>使用babylon.js添加3D模型</title>
<meta property="og:description" content="使用自定义样式图层和babylon.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 src="https://unpkg.com/babylonjs@5.42.2/babylon.js"></script>
<script src="https://unpkg.com/babylonjs-loaders@5.42.2/babylonjs.loaders.min.js"></script>
<div id="map"></div>
<script>
const BABYLON = window.BABYLON;
const map = (window.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 worldOrigin = [148.9819, -35.39847];
const worldAltitude = 0;
// Maplibre.js默认坐标系统(无旋转)
// +x东,-y北,+z上
//var worldRotate = [0, 0, 0];
// Babylon.js默认坐标系统
// +x东,+y上,+z北
const worldRotate = [Math.PI / 2, 0, 0];
// 计算墨卡托坐标和比例
const worldOriginMercator = maplibregl.MercatorCoordinate.fromLngLat(
worldOrigin,
worldAltitude
);
const worldScale = worldOriginMercator.meterInMercatorCoordinateUnits();
// 计算世界矩阵
const worldMatrix = BABYLON.Matrix.Compose(
new BABYLON.Vector3(worldScale, worldScale, worldScale),
BABYLON.Quaternion.FromEulerAngles(
worldRotate[0],
worldRotate[1],
worldRotate[2]
),
new BABYLON.Vector3(
worldOriginMercator.x,
worldOriginMercator.y,
worldOriginMercator.z
)
);
// 根据CustomLayerInterface配置3D模型的自定义图层
const customLayer = {
id: '3d-model',
type: 'custom',
renderingMode: '3d',
onAdd (map, gl) {
this.engine = new BABYLON.Engine(
gl,
true,
{
useHighPrecisionMatrix: true // 重要:防止墨卡托比例下的抖动
},
true
);
this.scene = new BABYLON.Scene(this.engine);
/**
* 可选添加
* this.scene.autoClearDepthAndStencil = false
* 并且对renderingGroupIds单独设置
* this.scene.setRenderingAutoClearDepthStencil(1,false)
* 以允许与maplibre场景混合
* 如https://doc.babylonjs.com/features/featuresDeepDive/scene/optimize_your_scene#reducing-calls-to-glclear中所述
*/
this.scene.autoClear = false;
/**
* 如果你只想与maplibre-gl交互而不需要babylonjs的指针事件,请使用detachControl。
* 或者用以下两行替换this.scene.detachControl(),它们将允许事件冒泡到maplibre-gl。
* this.scene.preventDefaultOnPointerDown = false
* this.scene.preventDefaultOnPointerUp = false
* https://doc.babylonjs.com/typedoc/classes/BABYLON.Scene#preventDefaultOnPointerDown
*/
this.scene.detachControl();
this.scene.beforeRender = () => {
this.engine.wipeCaches(true);
};
// 创建简单相机(其投影矩阵将手动计算)
this.camera = new BABYLON.Camera(
'Camera',
new BABYLON.Vector3(0, 0, 0),
this.scene
);
// 创建简单光源
const light = new BABYLON.HemisphericLight(
'light1',
new BABYLON.Vector3(0, 0, 100),
this.scene
);
light.intensity = 0.7;
// 添加调试坐标轴查看器,位于原点,轴长10米
new BABYLON.AxesViewer(this.scene, 10);
// 加载GLTF模型到场景中
BABYLON.SceneLoader.LoadAssetContainerAsync(
'https://maplibre.org/maplibre-gl-js/docs/assets/34M_17/34M_17.gltf',
'',
this.scene
).then((modelContainer) => {
modelContainer.addAllToScene();
const rootMesh = modelContainer.createRootMesh();
// 如果使用maplibre.js坐标系统(+z向上)
//rootMesh.rotation.x = Math.PI/2
// 创建第二个网格
const rootMesh2 = rootMesh.clone();
// 在babylon.js坐标系中定位
rootMesh2.position.x = 25; // +东,单位米
rootMesh2.position.z = 25; // +北,单位米
});
this.map = map;
},
render (gl, args) {
const cameraMatrix = BABYLON.Matrix.FromArray(args.defaultProjectionData.mainMatrix);
// 世界-视图-投影矩阵
const wvpMatrix = worldMatrix.multiply(cameraMatrix);
this.camera.freezeProjectionMatrix(wvpMatrix);
this.scene.render(false);
this.map.triggerRepaint();
}
};
map.on('style.load', () => {
map.addLayer(customLayer);
});
</script>
</body>
</html>