cesiumjs里得quaternion.js文件里实现了四元数的计算,关于四元数主要是用来描述三维空间里的旋转缩放变换的,一个四元数描述为q=a+bi+cj+dk,由一个实数和三个负数组成,三维变化有点不好理解,换成二维的就简单多了。
二维旋转变化可以用负数,a = b+ci描述,b描述为缩放比例,c描述为角度,可换成矩阵形式。
关于四元数,具体可以参考下面的链接:
如何形象地理解四元数?
var viewer = new Cesium.Viewer("cesiumContainer");
//Set the random number seed for consistent results.
Cesium.Math.setRandomNumberSeed(3);//随机数种子,不清楚啥用
//Set bounds of our simulation time
var start = Cesium.JulianDate.fromDate(new Date(2015, 2, 25, 16));//起始时间
var stop = Cesium.JulianDate.addSeconds(//结束时间
start,
120,
new Cesium.JulianDate()
);
//Make sure viewer is at the desired time.
viewer.clock.startTime = start.clone();//开始
viewer.clock.stopTime = stop.clone();//结束
viewer.clock.currentTime = start.clone();//当前时间为开始时刻
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP; //Loop at the end 循环结束后重新开始
viewer.clock.multiplier = 1;//倍速
viewer.clock.shouldAnimate = true;//开始播放,false为暂停
//Set timeline to simulation bounds
viewer.timeline.zoomTo(start, stop);//目前场景时间
var viewModel = {//动态数据
emissionRate: 5.0,
gravity: 0.0,
minimumParticleLife: 1.2,
maximumParticleLife: 1.2,
minimumSpeed: 1.0,
maximumSpeed: 4.0,
startScale: 1.0,
endScale: 5.0,
particleSize: 25.0,
};
Cesium.knockout.track(viewModel);//监控数据
var toolbar = document.getElementById("toolbar");
Cesium.knockout.applyBindings(viewModel, toolbar);//数据绑定控件
var entityPosition = new Cesium.Cartesian3();
var entityOrientation = new Cesium.Quaternion();//表示三维空间旋转的四维坐标(四元数)
var rotationMatrix = new Cesium.Matrix3();
var modelMatrix = new Cesium.Matrix4();
function computeModelMatrix(entity, time) {//根据时间计算实体的模型矩阵
return entity.computeModelMatrix(time, new Cesium.Matrix4());
}
var emitterModelMatrix = new Cesium.Matrix4();
var translation = new Cesium.Cartesian3();
var rotation = new Cesium.Quaternion();
var hpr = new Cesium.HeadingPitchRoll();
var trs = new Cesium.TranslationRotationScale();//仿射变化矩阵
function computeEmitterModelMatrix() {
hpr = Cesium.HeadingPitchRoll.fromDegrees(0.0, 0.0, 0.0, hpr);//旋转
trs.translation = Cesium.Cartesian3.fromElements(
-4.0,
0.0,
1.4,
translation
);//平移
trs.rotation = Cesium.Quaternion.fromHeadingPitchRoll(hpr, rotation);
return Cesium.Matrix4.fromTranslationRotationScale(
trs,
emitterModelMatrix
);
}
var pos1 = Cesium.Cartesian3.fromDegrees(//起始位置
-75.15787310614596,
39.97862668312678
);
var pos2 = Cesium.Cartesian3.fromDegrees(//结束位置
-75.1633691390455,
39.95355089912078
);
var position = new Cesium.SampledPositionProperty();
//不确定是不是在点之间进行插值,源代码看不懂
position.addSample(start, pos1);
position.addSample(stop, pos2);
var entity = viewer.entities.add({
availability: new Cesium.TimeIntervalCollection([//在给定时间内提供数据
new Cesium.TimeInterval({
start: start,
stop: stop,
}),
]),
model: {//模型URL和尺寸
uri: "../SampleData/models/CesiumMilkTruck/CesiumMilkTruck.glb",
minimumPixelSize: 64,
},
viewFrom: new Cesium.Cartesian3(-100.0, 0.0, 100.0),//偏移量
position: position,
orientation: new Cesium.VelocityOrientationProperty(position),//根据汽车速度计算四元数
});
viewer.trackedEntity = entity;//相机跟踪实体
var scene = viewer.scene;
var particleSystem = scene.primitives.add(//添加粒子系统
new Cesium.ParticleSystem({
image: "../SampleData/smoke.png",//粒子图片路径
startColor: Cesium.Color.LIGHTSEAGREEN.withAlpha(0.7),//粒子起始颜色
endColor: Cesium.Color.WHITE.withAlpha(0.0),//粒子结束颜色
startScale: viewModel.startScale,//开始尺寸,倍数
endScale: viewModel.endScale,//结束尺寸
minimumParticleLife: viewModel.minimumParticleLife,//粒子存活最短时间,秒
maximumParticleLife: viewModel.maximumParticleLife,//最长时间
minimumSpeed: viewModel.minimumSpeed,//速度
maximumSpeed: viewModel.maximumSpeed,
imageSize: new Cesium.Cartesian2(//粒子大小,像素
viewModel.particleSize,
viewModel.particleSize
),
emissionRate: viewModel.emissionRate,//每秒发射粒子数量,默认5
bursts: [//给定时间周期性地粒子爆炸,在粒子系统存活时间内
// these burst will occasionally sync to create a multicolored effect
new Cesium.ParticleBurst({//在第5、10、15秒会爆炸
time: 5.0,
minimum: 10,
maximum: 100,
}),
new Cesium.ParticleBurst({
time: 10.0,
minimum: 50,
maximum: 100,
}),
new Cesium.ParticleBurst({
time: 15.0,
minimum: 200,
maximum: 300,
}),
],
lifetime: 16.0,//粒子系统发射粒子时常,
loop:true,//默认true,若为false,粒子系统会在lifetime时间之后停止
//注:粒子系统的时间和viewer.clock的时间不绑定,模型运动的循环和粒子的循环不一致
emitter: new Cesium.CircleEmitter(2.0),//发射器大小和样式
emitterModelMatrix: computeEmitterModelMatrix(),//计算模型与世界的转换矩阵
updateCallback: applyGravity,//粒子回调函数
})
);
var gravityScratch = new Cesium.Cartesian3();
function applyGravity(p, dt) {
//dt 自上次以来的更新时间,秒
// We need to compute a local up vector for each particle in geocentric space.
var position = p.position;//粒子世界坐标系的位置
Cesium.Cartesian3.normalize(position, gravityScratch);//归一化
//粒子位置中z分量占比越高,粒子速度就越大
Cesium.Cartesian3.multiplyByScalar(
gravityScratch,
viewModel.gravity * dt,
gravityScratch
);//计算速度
p.velocity = Cesium.Cartesian3.add(
p.velocity,
gravityScratch,
p.velocity
);//更新粒子速度
}
viewer.scene.preUpdate.addEventListener(function (scene, time) {
//监听scene更新或渲染
//汽车位置改变时scence就要重新渲染
particleSystem.modelMatrix = computeModelMatrix(entity, time);//计算当前模型矩阵
// Account for any changes to the emitter model matrix.
particleSystem.emitterModelMatrix = computeEmitterModelMatrix();//计算粒子发射初始位置
// Spin the emitter if enabled.
if (viewModel.spin) {//旋转,空间没有spin属性,不执行
viewModel.heading += 1.0;
viewModel.pitch += 1.0;
viewModel.roll += 1.0;
}
});
//监测更新
Cesium.knockout
.getObservable(viewModel, "emissionRate")
.subscribe(function (newValue) {
particleSystem.emissionRate = parseFloat(newValue);
});
Cesium.knockout
.getObservable(viewModel, "particleSize")
.subscribe(function (newValue) {
var particleSize = parseFloat(newValue);
particleSystem.minimumImageSize.x = particleSize;
particleSystem.minimumImageSize.y = particleSize;
particleSystem.maximumImageSize.x = particleSize;
particleSystem.maximumImageSize.y = particleSize;
});
Cesium.knockout
.getObservable(viewModel, "minimumParticleLife")
.subscribe(function (newValue) {
particleSystem.minimumParticleLife = parseFloat(newValue);
});
Cesium.knockout
.getObservable(viewModel, "maximumParticleLife")
.subscribe(function (newValue) {
particleSystem.maximumParticleLife = parseFloat(newValue);
});
Cesium.knockout
.getObservable(viewModel, "minimumSpeed")
.subscribe(function (newValue) {
particleSystem.minimumSpeed = parseFloat(newValue);
});
Cesium.knockout
.getObservable(viewModel, "maximumSpeed")
.subscribe(function (newValue) {
particleSystem.maximumSpeed = parseFloat(newValue);
});
Cesium.knockout
.getObservable(viewModel, "startScale")
.subscribe(function (newValue) {
particleSystem.startScale = parseFloat(newValue);
});
Cesium.knockout
.getObservable(viewModel, "endScale")
.subscribe(function (newValue) {
particleSystem.endScale = parseFloat(newValue);
});
var options = [
{
text: "Circle Emitter",
onselect: function () {
particleSystem.emitter = new Cesium.CircleEmitter(2.0);
},
},
{
text: "Sphere Emitter",
onselect: function () {
particleSystem.emitter = new Cesium.SphereEmitter(2.5);
},
},
{
text: "Cone Emitter",
onselect: function () {
particleSystem.emitter = new Cesium.ConeEmitter(
Cesium.Math.toRadians(45.0)
);
},
},
{
text: "Box Emitter",
onselect: function () {
particleSystem.emitter = new Cesium.BoxEmitter(
new Cesium.Cartesian3(10.0, 10.0, 10.0)
);
},
},
];
Sandcastle.addToolbarMenu(options);