Cesium为我们提供了非常棒的烟花的粒子特效,我们可以在沙盒中的Particle System FireWorks.html中看到
考虑到篇幅问题,我会把自己认为比较关键的代码拿出来,进行分析
首先我们放重头戏,我们看看粒子系统是如何放入场景中的
scene.primitives.add(
new Cesium.ParticleSystem({
image: getImage(),
startColor: color,
endColor: color.withAlpha(0.0),
particleLife: life,
speed: 100.0,
imageSize: particlePixelSize,
emissionRate: 0,
emitter: new Cesium.SphereEmitter(0.1),
bursts: bursts,
lifetime: lifetime,
updateCallback: force,
modelMatrix: modelMatrix,
emitterModelMatrix: emitterModelMatrix,
})
);
我们可以看到,粒子发射器的发射速率为0个/s(即不发射粒子),而烟花效果依靠的是bursts,使其在一定的时间间隔中,瞬间释放大量粒子
我们看看getImage的过程:
这里使用canvas2D生成一张20*20像素的图片,图片中绘制了一个半径为8的圆
function getImage() {
if (!Cesium.defined(particleCanvas)) {
particleCanvas = document.createElement("canvas");
particleCanvas.width = 20;
particleCanvas.height = 20;
var context2D = particleCanvas.getContext("2d");
context2D.beginPath();
context2D.arc(8, 8, 8, 0, Cesium.Math.TWO_PI, true);
context2D.closePath();
context2D.fillStyle = "rgb(255, 255, 255)";
context2D.fill();
}
return particleCanvas;
}
如果你认真一些,你会发现这个圆其实并不是在20*20的图片的中心位置
颜色和偏移量、爆发时间间隔
color和粒子发射器的位置offset粒子爆发间隔burst的time都是系统根据参数在一定范围内生成的随机数
var colorOptions = [
{
minimumRed: 0.75,
green: 0.0,
minimumBlue: 0.8,
alpha: 1.0,
},
{
red: 0.0,
minimumGreen: 0.75,
minimumBlue: 0.8,
alpha: 1.0,
},
{
red: 0.0,
green: 0.0,
minimumBlue: 0.8,
alpha: 1.0,
},
{
minimumRed: 0.75,
minimumGreen: 0.75,
blue: 0.0,
alpha: 1.0,
},
];
for (var i = 0; i < numberOfFireworks; ++i) {
var x = Cesium.Math.randomBetween(xMin, xMax);
var y = Cesium.Math.randomBetween(yMin, yMax);
var z = Cesium.Math.randomBetween(zMin, zMax);
var offset = new Cesium.Cartesian3(x, y, z);
var color = Cesium.Color.fromRandom(
colorOptions[i % colorOptions.length]
);
var bursts = [];
for (var j = 0; j < 3; ++j) {
bursts.push(
new Cesium.ParticleBurst({
time: Cesium.Math.nextRandomNumber() * lifetime,
minimum: burstSize,
maximum: burstSize,
})
);
}
createFirework(offset, color, bursts);
}
而offset则用来计算出粒子的emitterModelMatrix,我们使用emitterModelMatrix用来表示粒子发射区域相对于modelMatrix的偏移量
这里通过在不同的位置,分别创建粒子系统,使得一个粒子系统负责一处位置、一种颜色的粒子发射效果
核心功能createFirework
function createFirework(offset, color, bursts) {
var position = Cesium.Cartesian3.add(emitterInitialLocation, offset, new Cesium.Cartesian3());
var emitterModelMatrix = Cesium.Matrix4.fromTranslation(position, emitterModelMatrixScratch);
var particleToWorld = Cesium.Matrix4.multiply(modelMatrix, emitterModelMatrix, new Cesium.Matrix4());
var worldToParticle = Cesium.Matrix4.inverseTransformation(particleToWorld, particleToWorld);
var size = Cesium.Math.randomBetween(minimumExplosionSize,maximumExplosionSize);
var particlePositionScratch = new Cesium.Cartesian3();
var force = function (particle) {
var position = Cesium.Matrix4.multiplyByPoint(
worldToParticle,
particle.position,
particlePositionScratch
);
if (Cesium.Cartesian3.magnitudeSquared(position) >= size * size) {
Cesium.Cartesian3.clone(
Cesium.Cartesian3.ZERO,
particle.velocity
);
}
};
var normalSize = (size - minimumExplosionSize) / (maximumExplosionSize - minimumExplosionSize);
var minLife = 0.3;
var maxLife = 1.0;
var life = normalSize * (maxLife - minLife) + minLife;
scene.primitives.add(
new Cesium.ParticleSystem({
image: getImage(),
startColor: color,
endColor: color.withAlpha(0.0),
particleLife: life,
speed: 100.0,
imageSize: particlePixelSize,
emissionRate: 0,
emitter: new Cesium.SphereEmitter(0.1),
bursts: bursts,
lifetime: lifetime,
updateCallback: force,
modelMatrix: modelMatrix,
emitterModelMatrix: emitterModelMatrix,
})
);
}
我们先看上面代码中的回调函数force,我们认真看就会发现,这个回调函数force其实非常的鸡肋,它通过计算发射出的粒子距离粒子发射器中心的距离,如果距离大于size,则让粒子的速度变成0,使其不再移动
particleToWorld作为粒子相对于世界坐标系的位置,而worldToParticle则是上一个矩阵的逆。因为每个粒子的位置都是世界坐标系下的位置,因此需要使用worldToParticle乘以这个粒子的位置,从而得到粒子相对于粒子发射器的位置。
可以看到这里根据offset计算出emitterModelMatrix,得到每个粒子系统最后的位置,而size则是粒子最大发射的距离(在数学上,因为有负数的存在,所以我们使用两个值的平方进行距离的比较)
因为初次写博客,文笔生涩,还望大家见谅,同时欢迎大家一起讨论学习。
如果觉得粒子系统的使用还是有难度,那么建议大家使用earthUI,在其中完全可以通过操作UI得到想要的效果(教程在bilibili)