最近做项目有一个三维需求使用CesiumJS比较难以实现,发现THREEJS中效果比较合适,于是准备将THREEJS整合到CesiumJS中
为实现效果所需我们找到官方Integrating Cesium with Three.js博客,于是根据该博客提供的思路去实现整合
文章目录
- 一、创建ThreeJS类
- 二、插件threeJs 优化
- 1、创建ThressScene工具
- 1.1、同步camera
- 1.2、同步场景中的物体
- 1.3、同步render
- 2、插件使用
- 2.1 引入插件初始化map
- 2.1 初始化Meshes
- 2.2 初始化threeScene
- 2.3 效果预览
一、创建ThreeJS类
为了开发时更好的扩展性把他写到了一个ThreeJS 类里面,功能直接调用即可
function ThreeJs(viewer, cesiumGlobal, defaultStatic, threeConf) {
if (viewer && threeConf) {
const { threeGlobal, containerId, threeContainerId } = threeConf;
Cesium = cesiumGlobal;
THREE = threeGlobal;
this._viewer = viewer;
this._containerId = containerId;
this._threeContainerId = threeContainerId;
this._initContainer();
this._initThree();
}
}
//部分省略......
项目中使用
import { ThreeJs } from 'cesium_dev_kit'
// 初始化map
initMap (){
const ThreeJsObj = new ThreeJs({
cesiumGlobal: Cesium,
threeGlobal: THREE,
containerId: 'cesiumContainer',
threeContainerId: 'threeContainer',
//部分省略......
})
this.c_viewer = ThreeJsObj.viewer;
this.threeJs = ThreeJsObj.threeJs;
this.initThree(this.threeJs);
},
// 初始化ThreeJs
initThree (ThreeJs) {
const threeDObject = this.create3DOject(ThreeJs, this.c_viewer);
ThreeJs.addThreeObjects(threeDObject)
this.c_viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)//解除视角锁定
},
// 创建三维模型
create3DOject (threeObj, viewer) {
let three = threeObj._three;
let _3Dobjects = [], _3DOB;
let entity = {
name: 'Polygon',
polygon: {
hierarchy: Cesium.Cartesian3.fromDegreesArray([
minWGS84[0], minWGS84[1],
maxWGS84[0], minWGS84[1],
maxWGS84[0], maxWGS84[1],
minWGS84[0], maxWGS84[1]
]),
material: Cesium.Color.RED.withAlpha(0.1)
}
}
let Polypon = viewer.entities.add(entity);
let doubleSideMaterial = new THREE.MeshNormalMaterial({
side: THREE.DoubleSide
});
//部分省略......
return _3Dobjects;
}
根据官网的思量整合后最终实现效果如下
效果是实现了,但是这个封装耦合性相当高,多个功能都需要使用这个插件时特别蹩脚,于是开始了下一次造…
二、插件threeJs 优化
理解官方案例的思路整合关键主要有三个方面,第一个是同步Camera,第二个是同步Scene中的物体位置,第三个是需要同步CesiumJS和THREEJS 的render重绘程序
1、创建ThressScene工具
为了扩展与管理我们抽离一个Scene 的工具js
class Scene extends THREE.Scene {}
在这个js 里面去实现插件操作的优化
1.1、同步camera
创建一个updateCameraMatrix来同步项目位置,核心代码如下
updateCameraMatrix() {
//同步相机
this.camera.fov = Cesium.Math.toDegrees(
this.cesiumViewer.camera.frustum.fovy
) // ThreeJS FOV is vertical
this.camera.updateProjectionMatrix()
this.camera.matrixAutoUpdate = false
const cvm = this.cesiumViewer.camera.viewMatrix
const civm = this.cesiumViewer.camera.inverseViewMatrix
this.camera.lookAt(this.cameraCenter)
//部分省略......
const width = this.cesiumViewer.scene.canvas.clientWidth
const height = this.cesiumViewer.scene.canvas.clientHeight
this.camera.aspect = width / height
this.camera.updateProjectionMatrix()
this.renderer.setSize(width, height)
return this
}
1.2、同步场景中的物体
创建一个updateGroupMatrixWorld来进行物体位置同步,核心代码如下
// 重写add 方法
add(object) {
if (arguments.length > 1) {
for (let i = 0; i < arguments.length; i++) {
this.childrenGroup.add(arguments[i])
}
return this
}
//部分省略......
return this
}
updateGroupMatrixWorld() {
// 得到面向模型的前向方向
const center = this.cartesian3ToVector(
Cesium.Cartesian3.fromDegrees(this.lngLat[0], this.lngLat[1], 0)
)
// 使用从左下到左上的方向作为上向量
const topLeft = this.cartesian3ToVector(
Cesium.Cartesian3.fromDegrees(this.lngLat[0], this.lngLat[1], 2)
)
const latDir = new THREE.Vector3().subVectors(center, topLeft).normalize()
// 配置实体的位置和方向
// this.syncGroup.position.copy(center)
this.syncGroup.lookAt(latDir)
this.syncGroup.up.copy(latDir)
this.syncGroup.updateMatrix()
this.cameraOffset.copy(center)
this.sphere.position.set(0 - center.x, 0 - center.y, 0 - center.z)
this.syncGroup.up.set(0, 0, -1)
this.up.set(0, 0, -1)
return this
}
1.3、同步render
renderCesium() {
this.cesiumViewer.render()
return this
}
renderThree() {
this.renderer.render(this, this.camera)
return this
}
loop(callback) {
const _loop = function () {
let time = requestAnimationFrame(_loop)
callback && callback(time)
}
_loop()
}
2、插件使用
插件改完了心里还是有点鸡冻,马上在项目中引入开始测试
2.1 引入插件初始化map
import { initCesium } from 'cesium_dev_kit'
// 初始化map
initMap (){
const { viewer, threeJs, base, graphics, material } = new initCesium({
cesiumGlobal: Cesium,
threeGlobal: THREE,
containerId: 'cesiumContainer',
threeContainerId: 'threeContainer',
//部分省略......
})
this.c_viewer = viewer;
this.threeJs = threeJs;
this.base = base;
this.graphics = graphics;
this.material = material
this.initThree(this.threeJs);
// this.getClickPosition()
this.createAEllipsoid();
this.createAFanShape();
},
2.1 初始化Meshes
这里我们就使用官方的extrude案例
initMeshes (scene) {
// 环形 extrude
const closedSpline = new THREE.CatmullRomCurve3([
new THREE.Vector3(-60, 30, 60), // 左下
new THREE.Vector3(-60, 100, 60), // 左中
new THREE.Vector3(-60, 220, 60), // 左上
new THREE.Vector3(60, 80, -60), // 右中
new THREE.Vector3(60, 30, -60), // 右下
]);
// 2、extrude settings
closedSpline.curveType = "catmullrom";
closedSpline.closed = true;
const extrudeSettings = {
steps: 100,
bevelEnabled: false,
extrudePath: closedSpline,
};
// 3、construct shape
const r = 20; // 截面半径
const pts1 = [];
const count = 3; // 截面的棱边数量
for (let index = 0; index < count; index++) {
// index/count 几分之几,2π为周长
const a = (index / count) * Math.PI * 2;
pts1.push(new THREE.Vector2(r * Math.cos(a), r * Math.sin(a)));
}
const shape1 = new THREE.Shape(pts1);
// create geometry
const geometry1 = new THREE.ExtrudeGeometry(shape1, extrudeSettings);
// create material
const material1 = new THREE.MeshLambertMaterial({
color: 0xb00000,
});
// assembly meshes
const mesh1 = new THREE.Mesh(geometry1, material1);
// add mesh to scene
scene.add(mesh1);
//部分省略......
},
2.2 初始化threeScene
initThree (ThreeJs) {
const { scene, camera } = ThreeJs.initThree({ center, axesHelper: true, threeHabit: false });
this.initLight(scene, camera)
this.initMeshes(scene);
this.flyto(scene);
ThreeJs.loop(function () {
scene.update();
})
},
2.3 效果预览
光线投射
图形挤压烟花效果
奔跑的士兵
嗯嗯测试了几个功能看了基本上问题不大了,ok先告一段落后期继续优化…
感谢您的阅读,最后附上插件下载与源码地址
- 插件下载安装
npm install cesium_dev_kit
- 案例源码地址
https://github.com/dengxiaoning/cesium_dev_kit