Integrate With CesiumJS
CesiumJS is an open source JavaScript library for creating world-class 3D globes and maps with the best possible performance, precision, visual quality, and ease of use. Because both BabylonJS and CesiumJS render 3D scenes with JavaScript, it is possible to integrate them for a better visual effect.
Inspired by Integrating Cesium with Three.js, this solution renders the two libraries on two canvases, with the upper canvas used for BabylonJS. Set the upper canvas to pointer-events: none; so CesiumJS keeps control, then make BabylonJS's camera follow CesiumJS's camera.
Setup CesiumJS
const LNG = -122.4175, LAT = 37.655;
const viewer = new Cesium.Viewer('cesiumContainer', { terrainProvider: Cesium.createWorldTerrain(), useDefaultRenderLoop: false});
viewer.camera.flyTo({ destination : Cesium.Cartesian3.fromDegrees(LNG, LAT, 300), orientation : { heading : Cesium.Math.toRadians(0.0), pitch : Cesium.Math.toRadians(-90.0), }});
// get these two points for later setup of BabylonJS root node.const base_point = cart2vec(Cesium.Cartesian3.fromDegrees(LNG, LAT, 50));const base_point_up = cart2vec(Cesium.Cartesian3.fromDegrees(LNG, LAT, 300));Setup BabylonJS
The scene's clearColor is important; make the background transparent so you can see what is rendered in CesiumJS.
import { FreeCamera } from "@babylonjs/core/Cameras/freeCamera";import { Engine } from "@babylonjs/core/Engines/engine";import { Color4 } from "@babylonjs/core/Maths/math.color";import { Vector3 } from "@babylonjs/core/Maths/math.vector";import { Scene } from "@babylonjs/core/scene";const engine = new Engine(canvas);const scene = new Scene(engine);scene.clearColor = Color4(0, 0, 0, 0);const camera = new FreeCamera("camera", new Vector3(0, 0, -10), scene);Rendering Sequence
Before BabylonJS renders, we should change BabylonJS's camera position and rotation so they exactly match CesiumJS's.
import { Matrix, Vector3 } from "@babylonjs/core/Maths/math.vector";function cart2vec(cart) { return new Vector3(cart.x, cart.z, cart.y);}const fov = Cesium.Math.toDegrees(viewer.camera.frustum.fovy);camera.fov = fov / 180 * Math.PI;const civm = viewer.camera.inverseViewMatrix;const camera_matrix = Matrix.FromValues(civm[0], civm[1], civm[2], civm[3], civm[4], civm[5], civm[6], civm[7], civm[8], civm[9], civm[10], civm[11], civm[12], civm[13], civm[14], civm[15]);// I'm sure it is able to get the result with the matrix directly, but I'm not familiar with matrixs, so you can do it yourselves.:)const scaling = Vector3.Zero(), rotation = Vector3.Zero(), transform = Vector3.Zero();camera_matrix.decompose(scaling, rotation, transform);const camera_pos = cart2vec(transform), camera_direction = cart2vec(viewer.camera.direction), camera_up = cart2vec(viewer.camera.up);const rotation_y = Math.atan(camera_direction.z / camera_direction.x);if (camera_direction.x < 0) rotation_y += Math.PI;rotation_y = Math.PI / 2 - rotation_y;const rotation_x = Math.asin(-camera_direction.y);const camera_up_before_rotatez = new Vector3(-Math.cos(rotation_y), 0, Math.sin(rotation_y));const rotation_z = Math.acos(camera_up.x * camera_up_before_rotatez.x + camera_up.y * camera_up_before_rotatez.y + camera_up.z * camera_up_before_rotatez.z);rotation_z = Math.PI / 2 - rotation_z;if (camera_up.y < 0) rotation_z = Math.PI - rotation_z;camera.position.x = camera_pos.x - base_point.x;camera.position.y = camera_pos.y - base_point.y;camera.position.z = camera_pos.z - base_point.z;camera.rotation.x = rotation_x;camera.rotation.y = rotation_y;camera.rotation.z = rotation_z;CesiumJS must render first because we need its camera parameters.
engine.runRenderLoop(() => { viewer.render(); moveBabylonCamera(); scene.render();});Setup root node
This step is not strictly necessary, but it makes things easier.
import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial";import { Color3 } from "@babylonjs/core/Maths/math.color";import { MeshBuilder } from "@babylonjs/core/Meshes/meshBuilder";import { TransformNode } from "@babylonjs/core/Meshes/transformNode";const root_node = new TransformNode("BaseNode", scene);root_node.lookAt(base_point_up.subtract(base_point));root_node.addRotation(Math.PI / 2, 0, 0);// Then you can add your meshes to root_node.const box = MeshBuilder.CreateBox("box", { size: 10 }, scene);const material = new StandardMaterial("Material", scene);material.emissiveColor = new Color3(1, 0, 0);material.alpha = 0.5;box.material = material;box.parent = root_node;const ground = MeshBuilder.CreateGround("ground", { width: 100, height: 100}, scene);ground.material = material;ground.parent = root_node;Example
An example can be found here: cesium-babylonjs.