Cesium中3D模型的驱动方法

Cesium中3D模型的运动可以使用CZML直接驱动,但使用该方法前提是能事先计算出3D模型的运动轨迹,具有很大的局限性。那么如何实时驱动3D模型呢?

Cesium渲染过程分析

Cesium渲染过程的分析可以参考链接1。Cesium的渲染始于虚拟地球组件CesiumWidget的startRenderLoop方法,在该方法中将调用requestAnimationFrame函数开始渲染。

虚拟地球组件CesiumWidget包含Scene组件,在其render方法随后调用Scene的render方法。在场景组件Scene的render方法中提供了preUpdate、postUpdate、preRender、postRender四个事件对象,这四个事件对象将是我们实时驱动3D模型的关键。

preUpdate事件

在Cesium更新渲染周期开始之前以目标帧率触发preUpdate事件。

1
2
3
4
5
6
scene.postUpdate.addEventListener(function() {
// This code will run at 60 FPS
if (changeToPromptRender) {
scene.requestRender();
}
});

postUpdate事件

在场景更新之后,新帧渲染之前以目标帧率触发postUpdate事件。

preRender事件

在场景更新之后,新帧渲染之前触发preRender事件。

1
2
3
4
scene.preRender.addEventListener(function() {
// This code will run when a new frame is rendered
// including when changeToPromptRender is true
});

postRender事件

在新帧渲染之后触发postRender事件。

实时驱动3D模型

从上一节对Cesium渲染过程分析可知,要实时驱动3D模型,应订阅场景Scene的preUpdate事件,在preUpdate事件的处理函数中改变3D模型的位置和姿态。

订阅preUpdate事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//订阅场景的preUpdate事件
viewer.scene.preUpdate.addEventListener(function(scene, time) {
speedVector = Cesium.Cartesian3.multiplyByScalar(Cesium.Cartesian3.UNIT_X, speed / 10, speedVector);
position = Cesium.Matrix4.multiplyByPoint(planePrimitive.modelMatrix, speedVector, position);
pathPosition.addSample(Cesium.JulianDate.now(), position);
Cesium.Transforms.headingPitchRollToFixedFrame(position, hpRoll, Cesium.Ellipsoid.WGS84, fixedFrameTransform, planePrimitive.modelMatrix);

if (fromBehind.checked) {
// Zoom to model
Cesium.Matrix4.multiplyByPoint(planePrimitive.modelMatrix, planePrimitive.boundingSphere.center, center);
hpRange.heading = hpRoll.heading;
hpRange.pitch = hpRoll.pitch;
camera.lookAt(center, hpRange);
}
});

获取czml实体的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
viewer.scene.preUpdate.addEventListener(function(scene, time) {
//target是czml实体对象
//获取time时刻target的位置参数
target.position.getValue(time,position)
console.log(position)

target.orientation.getValue(time,orientation)
console.log(orientation)
});

viewer.scene.preUpdate.addEventListener(function(scene, time) {
var pos=target.position.getValue(time)
console.log(pos.toString())
//世界坐标转经纬高
var cartographicPosition = Cesium.Ellipsoid.WGS84.cartesianToCartographic(pos);
console.log(cartographicPosition.toString())
});

使用Entity API加载3D模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var viewer = new Cesium.Viewer('cesiumContainer');
var center=Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706,100);
var hpr=Cesium.HeadingPitchRoll.fromDegrees(90,-90,0);
//从headingPitchRoll转四元数
var quatern=Cesium.Transforms.headingPitchRollQuaternion(center,hpr);

var entity = viewer.entities.add({
position :center ,
orientation:quatern ,
model : {
uri : '../../../../Apps/SampleData/models/missile.glb',
scale: 0.1,
minimumPixelSize:50,
maximumScale:5000
}
});
viewer.trackedEntity = entity;

使用Primitive API加载3d模型

1
2
3
4
5
6
7
8
9
10
11
12
var viewer = new Cesium.Viewer('cesiumContainer');

var center1=Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706,100);
var hpr1=Cesium.HeadingPitchRoll.fromDegrees(90,-90,100);
//计算模式矩阵,实现本地坐标系坐标到世界坐标系坐标的转换
var modelMatrix=Cesium.Transforms.headingPitchRollToFixedFrame(center1, hpr1)

var model = viewer.scene.primitives.add(Cesium.Model.fromGltf({
url : '../../../../Apps/SampleData/models/missile.glb',
modelMatrix : modelMatrix,
scale : 1.0
}));

参考链接

  1. Cesium摄像头跟踪飞机实体时晃动问题分析,by jack huang.
  2. Improving Performance with Explicit Rendering, by Gabby Getz.
  3. Cesium 源码打包入门 [ver1.72] ,by mob604756f3c518.