概述
three + cesium 深度融合
思路是 使用两个dom叠加,three的dom在cesium的dom上, 将three的dom的鼠标事件禁用 pointer-events:none;, three的原点设置为cesium显示的坐标区域的中心点,three的相机同步到cesium的相机位置,操作由cesium反馈到three
html
<body>
<div id="cesiumContainer"></div>
<div id="threeContainer"></div>
</body>
css
#cesiumContainer,
#threeContainer {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
position: absolute;
top: 0;
left: 0;
}
#threeContainer {
pointer-events: none;
}
javascript
import * as Cesium from 'cesium';
import * as THREE from 'three';
const minWGS84 = [115.39, 38.9];
const maxWGS84 = [117.39, 40.9];
const _3Dobjects = [];
let viewer;
let three = {
scene: null,
camera: null,
renderer: null,
}
// 创建cesium 地球
function initCesium(){
viewer = new Cesium.Viewer('cesiumContainer',{
infoBox: false,
})
// 得到中心点
const center = Cesium.Cartesian3.fromDegrees(
this.centerWGS84.length > 0 ? this.centerWGS84[0] : (this.minWGS84[0] + this.maxWGS84[0]) / 2,
this.centerWGS84.length > 0 ? this.centerWGS84[1] : ((this.minWGS84[1] + this.maxWGS84[1]) / 2) - 1,
this.centerHigh
);
// 相机移动到中心点并设置朝向
viewer.camera.flyTo({
destination: center,
orientation: {
heading: Cesium.Math.toRadians(0),
pitch: Cesium.Math.toRadians(-60),
roll: Cesium.Math.toRadians(0)
},
duration: 3
});
}
// 创建three
function initThree(){
var fov = 45;
var width = window.innerWidth;
var height = window.innerHeight;
var aspect = width / height;
var near = 1;
var far = 10 * 1000 * 1000; // needs to be far to support Cesium's world-scale rendering
three.sence = new THREE.Sence();
three.camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
three.renderer = new THREE.WebGLRenderer({
alpha: true
});
document.getElementById('threeContainer').appendChild(three.renderer.domElement)
}
// 创建three 3d物体
function initObject3d(){
let geometry = new THREE.BoxBufferGeometry(1, 1, 1);
let material = new THREE.MeshBasicMaterial({
color: 0x00ff00,
});
let mesh = new THREE.Mesh(geometry, material);
// 放大物体
mesh.scale.set(1000, 1000, 1000); // 放大
mesh.position.set(0, 0, 500); // 平移
let meshGroup = new THREE.Group();
meshGroup.add(mesh);
// 添加至场景
three.scene.add(meshGroup);
_3Dobjects.push({
threeMesh: meshGroup,
minWGS84,
maxWGS84
})
}
function loop() {
requestAnimationFrame(loop);
// cesium渲染
renderCesium();
// three.js渲染
renderThree();
}
function renderCesium() {
cesium.viewer.render();
}
function renderThree() {
// 设置相机跟cesium保持一致
three.camera.fov = Cesium.Math.toDegrees(cesium.viewer.camera.frustum.fovy);
// 声明一个将cesium框架的cartesian3转换为three.js的vector3(笛卡尔坐标转换为三维向量)
let cartToVec = function (cart) {
return new THREE.Vector3(cart.x, cart.y, cart.z);
};
// 将3D的物体通过经纬度转换成对应的位置
_3Dobjects.forEach((item, index) => {
// 通过经纬度获取中心点的位置
let center = Cesium.Cartesian3.fromDegrees(
(item.minWGS84[0] + item.maxWGS84[0]) / 2,
(item.minWGS84[1] + item.maxWGS84[1]) / 2
);
item.threeMesh.position.copy(cartToVec(center));
//计算朝向(切面方向-切线向量)
//中心高度点
let centerHeight = Cesium.Cartesian3.fromDegrees(
(item.minWGS84[0] + item.maxWGS84[0]) / 2,
(item.minWGS84[1] + item.maxWGS84[1]) / 2,
1
);
//左下
let bottomLeft = cartToVec(Cesium.Cartesian3.fromDegrees(item.minWGS84[0], item.minWGS84[1]));
//左上
let topLeft = cartToVec(Cesium.Cartesian3.fromDegrees(item.minWGS84[0], item.maxWGS84[1]));
//朝向()
let latDir = new THREE.Vector3().subVectors(bottomLeft, topLeft).normalize();
//设置查看方向
item.threeMesh.lookAt(cartToVec(centerHeight));
//设置朝向
item.threeMesh.up.copy(latDir);
});
//设置摄像机矩阵
// 设置相机跟cesium保持一致
three.camera.matrixAutoUpdate = false; //自动更新
//复制cesium相机矩阵
let cvm = cesium.viewer.camera.viewMatrix;
let civm = cesium.viewer.camera.inverseViewMatrix;
// three相机默认朝向0,0,0
three.camera.lookAt(0, 0, 0);
// 设置threejs相机矩阵
three.camera.matrixWorld.set(
civm[0],civm[4],civm[8],civm[12],
civm[1],civm[5],civm[9],civm[13],
civm[2],civm[6],civm[10],civm[14],
civm[3],civm[7],civm[11],civm[15]
);
three.camera.matrixWorldInverse.set(
cvm[0],cvm[4],cvm[8],cvm[12],
cvm[1],cvm[5],cvm[9],cvm[13],
cvm[2],cvm[6],cvm[10],cvm[14],
cvm[3],cvm[7],cvm[11],cvm[15]
);
const cesiumContainer = document.getElementById('cesiumContainer');
//设置宽高比例
let width = cesiumContainer.clientWidth;
let height = cesiumContainer.clientHeight;
three.camera.aspect = width / height;
//更新相机矩阵
three.camera.updateProjectionMatrix();
//设置尺寸大小
three.renderer.setSize(width, height);
three.renderer.clear();
three.renderer.render(three.scene, three.camera);
}
绑定点击事件
// 绑定点击事件
bindClick(viewer,three)
function bindClick(viewer,three){
viewer.scene.globe.depthTestAgainstTerrain = true;
new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas).setInputAction(function (pick: any) {
// 获取cesium中点击的经纬度
const res = getCartographic(viewer, pick.position);
console.log(res)
// 获取cesium中点击的物体
const pickObj = viewer.scene.pick(pick.position);
console.log(pickObj);
// 获取three中点击的坐标和物体
const threeRes = getThreeClick(viewer,three,pick.position)
console.log(threeRes);
if (Cesium.defined(pickObj) && Cesium.defined(pickObj.id)) {
if (pickObj.id === selected) return;
Cesium.defined(selected) && (selected = void 0);
}
// 相机移动到cesium点击的图元位置
if (Cesium.defined(pickObj) && Cesium.defined(pickObj.primitive) && Cesium.defined(pickObj.id)
&& Cesium.defined(pickObj.primitive.getGeometryInstanceAttributes)) {
selected = pickObj.id;
look = selected.look
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(look.center[0], look.center[1], Number(res.split(',')[2]) + 200),
orientation: {
heading: look.orientation.heading, //旋转角 正东为90°
pitch: Cesium.Math.toRadians(look.orientation.pitch), //俯仰角,水平为0°
roll: look.orientation.roll //翻滚角
},
})
const name = typeof (selected) == 'string' ? selected : selected.name;
console.log('用于demo的建筑,' + name)
} else {
Cesium.defined(selected) && (selected = void 0)
}
},Cesium.ScreenSpaceEventType.LEFT_CLICK)
}
// 坐标转换(鼠标坐标转换为经纬度)
function getCartographic(viewer: any, position: any) {
const ray: any = viewer.scene.camera.getPickRay(position);
let cartesian: any = null;
let pickPostion: any = {};
const feature = viewer.scene.pick(position);
if (viewer.scene.pickPositionSupported && Cesium.defined(feature) && feature.content) {
cartesian = viewer.scene.pickPosition(position);
} else if (feature instanceof Cesium.Cesium3DTileFeature) {
cartesian = viewer.scene.pickPosition(position);
} else {
// cartesian = viewer.scene.globe.pick(ray, viewer.scene);
cartesian = viewer.scene.pickPosition(position);
}
if (cartesian) {
const cartographic = Cesium.Cartographic.fromCartesian(cartesian); // 结果对象中的值将以弧度表示。
const longitude = Number(Cesium.Math.toDegrees(cartographic.longitude));
const latitude = Number(Cesium.Math.toDegrees(cartographic.latitude));
const height = Number(cartographic.height);
pickPostion = [longitude.toFixed(6), latitude.toFixed(6), height.toFixed(2)].join(',');
}
return pickPostion;
}
function getThreeClick(Viewer,three,position) {
const raycaster: any = new THREE.Raycaster();
const pointer: any = new THREE.Vector2();
const cesiumP = Viewer.scene.canvas.getBoundingClientRect()
const { width, height, top, left } = three.renderer.domElement.getBoundingClientRect()
const offsetx = three.renderer.domElement.offsetLeft
const offsety = three.renderer.domElement.offsetTop
const x = position.x + cesiumP.x - left + offsetx;
const y = position.y + cesiumP.y - top + offsety;
pointer.x = (x / width) * 2 - 1;
pointer.y = - (y / height) * 2 + 1;
const clickObjects = three.sence.children;
// 通过摄像机和鼠标位置更新射线
raycaster.setFromCamera(pointer, three.camera);
// 计算物体和射线的焦点
const intersects = raycaster.intersectObjects(clickObjects);
return intersects[0]
}
最后
以上就是文静仙人掌为你收集整理的three + cesium 深度融合three + cesium 深度融合的全部内容,希望文章能够帮你解决three + cesium 深度融合three + cesium 深度融合所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复