在初看到这个例子时,感觉这个例子没有什么可学的。仔细看完之后,至少可以学习到两个方面的内容:(1)基本的相机动画实现;(2)cesium相机绑定,即把相机绑定到模型上,当模型运动时,带着相机一起运动;
start:开始时间,设置动画开始时间,只是一个与结束时间相对的时间,设置到哪年并不影响动画。
stop:结束时间,也就是开始时间加上动画时长,时间最好是等于或略大于动画时长,否则,可能会导致动画不完整。
同样的动画数据,设置不同的动画时长,如下对:
动画时长370,正常动画时长 动画时长180,不正常动画时长从上图可以看出设置的结束动画时间提前于第n帧动画时间时,第n帧和后面的帧都会被剪切掉。绘制的路径也是不完整的。
动画基本设置代码如下:
var start = Cesium.JulianDate.fromDate(new Date(2015, 2, 25, 16));
var stop = Cesium.JulianDate.addSeconds(
start,
370,
new Cesium.JulianDate()
);
// 给viewer.clock的开始时间、结束时间、当前时间赋值
viewer.clock.startTime = start.clone();
viewer.clock.stopTime = stop.clone();
viewer.clock.currentTime = start.clone();
// 设置动画模式:结束后再循环
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP;
viewer.clock.multiplier = 1.0;
viewer.clock.shouldAnimate = true;
代码如下:
function addOneBall () {
var entity = viewer.entities.add({
availability: new Cesium.TimeIntervalCollection([
new Cesium.TimeInterval({
start: start,
stop: stop,
}),
]),
position: computeCirclularFlight(longitude, latitude, radius),
//绘制路径
path: {
material: Cesium.Color.RED, //点位颜色
width: 4 //像素点大小
},
model: {
uri: modelURI,
minimumPixelSize: 64,
},
});
entity.position.setInterpolationOptions({
interpolationDegree: 2,
interpolationAlgorithm: Cesium.HermitePolynomialApproximation,
});
postUpdate(entity)
}
function computeCirclularFlight (lon, lat, radius) {
var property = new Cesium.SampledPositionProperty();
var startAngle = Cesium.Math.nextRandomNumber() * 360.0;
var endAngle = startAngle + 360.0;
var increment = (Cesium.Math.nextRandomNumber() * 2.0 - 1.0) * 10.0 + 45.0;
for (var i = startAngle; i < endAngle; i += increment) {
var radians = Cesium.Math.toRadians(i);
var timeIncrement = i - startAngle;
var time = Cesium.JulianDate.addSeconds(
start,
timeIncrement,
new Cesium.JulianDate()
);
var position = Cesium.Cartesian3.fromDegrees(
lon + radius * 1.5 * Math.cos(radians),
lat + radius * Math.sin(radians),
Cesium.Math.nextRandomNumber() * 500 + 1800
);
property.addSample(time, position);
}
return property;
}
其中:availability:定义了动画开始与结束时间;computeCirclularFlight函数获取动画路径,并设置在每一个节点(关键帧)的动画时间。这里的动画时间就与前面的设置动画时间一起作用的,cesium会自动在两个关键帧之间内插相应的节点出,以免出现跳动现象,而通过 var position=entity.position.getValue(time)能够获取到该时刻下的位置;entity.position.setInterpolationOptions是使用动画插值函数,使用动画在节点转拐节点更加平滑。
postUpdate(entity)就是下面要讲的机绑定。
上面代码中加载完成动画后,有一个postUpdate(entity),是在这个函数中完成相机绑定。也就是在每一帧渲染中都设置相机。代码如下:
function postUpdate (entity) {
let camera = viewer.camera
viewer.scene.preUpdate.addEventListener(function (scene, time) {
// 获取对应时刻下,物体的位置
var position = entity.position.getValue(time);
if (!Cesium.defined(position)) {
return;
}
var transform;
//如果物体没有定义方向
if (!Cesium.defined(entity.orientation)) {
// 获取entity坐标position的z轴垂直于地表,entity坐标的y轴指向正北的4x4变换矩阵
transform = Cesium.Transforms.eastNorthUpToFixedFrame(position);
} else {//如果物体已经定义方向,则获取该时刻下的方向。
var orientation = entity.orientation.getValue(time);
if (!Cesium.defined(orientation)) {
return;
}
transform = Cesium.Matrix4.fromRotationTranslation(
Cesium.Matrix3.fromQuaternion(orientation),
position
);
}
// 保存相机姿态
var offset = Cesium.Cartesian3.clone(camera.position);
var direction = Cesium.Cartesian3.clone(camera.direction);
var up = Cesium.Cartesian3.clone(camera.up);
/// 设置相机为模型参考系
camera.lookAtTransform(transform);
Cesium.Cartesian3.clone(offset, camera.position);
Cesium.Cartesian3.clone(direction, camera.direction);
Cesium.Cartesian3.clone(up, camera.up);
Cesium.Cartesian3.cross(direction, up, camera.right);
});
}
其中最重要的是:camera.lookAtTransform(transform);官网解释这个函数是,Sets the camera position and orientation using a target and transformation matrix,使用目标和旋转矩阵设置相机位置和姿态。但是不能理解为什么在使用矩阵后,还要重置相机位置、姿态。先这样用,后面搞明白了再来补。