1.引入相关依赖,构建mapbox地图场景
import mapboxgl from 'mapbox-gl';
import {onMounted} from "vue";
import * as BABYLON from 'babylonjs';
mapboxgl.accessToken = 'pk.eyJ1Ijoibm9haDk3MTMiLCJhIjoiY2p4aXppNXZoMTc4YTN5bzhzMWY0emw3cyJ9.7GhaVOSALR-DwV5Lpn_h4Q';
const map = new mapboxgl.Map({
container: 'mapContainer',
style: 'mapbox://styles/mapbox/streets-v11',
zoom: 18,
center: [120, 31],
pitch: 60,
antialias: true // create the gl context with MSAA antialiasing, so custom layers are antialiased
});
2.根据CustomLayerInterface定义一个BabylonLayer
class BabylonLayer implements CustomLayerInterface {
readonly id: string;
readonly type: 'custom' = 'custom';
readonly renderingMode: '3d' = '3d';
private map: Map | undefined;
private scene: BABYLON.Scene | undefined;
private camera: BABYLON.Camera | undefined;
constructor(id: string) {
this.id = id;
}
onAdd = (map: Map, gl: WebGLRenderingContext) => {
this.map = map;
const engine = new BABYLON.Engine(gl, true, {
useHighPrecisionMatrix: true
}, true);
this.scene = new BABYLON.Scene(engine);
this.scene.autoClear = false;
this.scene.detachControl();
this.scene.beforeRender = function () {
engine.wipeCaches(true);
};
this.scene.ambientColor = new BABYLON.Color3(1, 1, 1);
this.camera = new BABYLON.Camera("mapbox-camera", new BABYLON.Vector3(), this.scene);
const light = new BABYLON.HemisphericLight("mapbox-light", new BABYLON.Vector3(0.5, 0.5, 4000), this.scene);
const points: Array<[number, number, number]> = [
[120, 31.3, 0],
[120.2, 31.2, 2000],
[120.2, 31.1, 4000],
[120.3, 31.0, 4000],
[120, 31.1, 8000],
[120, 31.3, 0]
];
createRibbonPolygon(points, this.scene);
}
render = (gl: WebGLRenderingContext, matrix: number[]) => {
const cameraMatrix = BABYLON.Matrix.FromArray(matrix);
this.camera!.freezeProjectionMatrix(cameraMatrix);
this.scene!.render(false);
this.map!.triggerRepaint();
}
}
3.定义创建多边形的方法
function createRibbonPolygon(points: Array<[number, number, number]>, scene: BABYLON.Scene) {
const pathPoints = points.map((p) => {
const mercatorCoord = mapboxgl.MercatorCoordinate.fromLngLat([p[0], p[1]], p[2]);
// 将经纬度x,y转换成babylon的坐标
return new BABYLON.Vector3(mercatorCoord.y, mercatorCoord.z, mercatorCoord.x);
});
const halfCount = Math.floor(pathPoints.length / 2)
const path0 = pathPoints.slice(0, halfCount)
const path1 = pathPoints.slice(halfCount).reverse()
// 构建ribbonMesh需要相同长度的数组
if (pathPoints.length % 2 === 1) path1.shift()
const ribbonMesh = BABYLON.MeshBuilder.CreateRibbon('ribbon-polygon', {
pathArray: [path0, path1],
// sideOrientation: BABYLON.Mesh.DOUBLESIDE 若设置这个双面都是黑色,材质不生效
}, scene);
// 定义材质
let myMaterial = new BABYLON.StandardMaterial("myMaterial", scene);
// 设置漫反射颜色为红色
myMaterial.diffuseColor = new BABYLON.Color3(1, 0, 0);
// 给 ribbonMesh 设置材质
ribbonMesh.material = myMaterial;
// 设置 myMaterial 成为双面材质
myMaterial.backFaceCulling = false;
// 旋转到xy的平面
ribbonMesh.rotateAround(BABYLON.Vector3.Zero(), new BABYLON.Vector3(1, 0, 0), Math.PI / 2)
.rotateAround(BABYLON.Vector3.Zero(), new BABYLON.Vector3(0, 0, 1), Math.PI / 2);
}
4.添加BabylonLayer至mapbox场景
map.on('style.load', () => {
const babylonLayer = new BabylonLayer('babylon-layer');
map.addLayer(babylonLayer);
});
实现效果: