在三維GIS系統(tǒng)中,我們經(jīng)常會看到一些常見的特效,比如雨雪霧天氣、煙火、爆炸、噴泉等,這些都可以通過粒子系統(tǒng)實現(xiàn),今天我就來帶領(lǐng)大家學習一下Cesium中的粒子系統(tǒng)。
1.什么是粒子系統(tǒng)
粒子系統(tǒng)是一種圖形技術(shù),可以模擬復雜的物理效果。粒子系統(tǒng)是小圖像的集合,即由一堆很小的圖片組成,當把它們聚集到一起觀看時,就會形成一個復雜的“模糊不清”的物體,如火、煙、天氣或煙花等。這些復雜的效果其實是通過控制每一個粒子對象的初始位置、速度、生命周期等行為來完成的,可以說粒子系統(tǒng)控制著每個粒子對象隨著時間的顯示和變化。
2.粒子系統(tǒng)創(chuàng)建示例及參數(shù)說明
首先,我們從一個簡單的示例說起。實現(xiàn)代碼如下:
//初始化設(shè)置ParticleSystem的各項參數(shù),因為這是一個動態(tài)變化的粒子,所以有
start、end和min、max等參數(shù)設(shè)置
const particleSystem = scene.primitives.add(
new Cesium.ParticleSystem({
//每個粒子的圖像
image: "../../SampleData/smoke.png",
//設(shè)置發(fā)射出的圖像大小
imageSize: new Cesium.Cartesian2(
viewModel.particleSize,
viewModel.particleSize
),
//startColor endColor,代替color使得粒子的顏色在粒子的生命過程中會在
這兩種顏色之間平滑地混合。
startColor: Cesium.Color.LIGHTSEAGREEN.withAlpha(0.7),
endColor: Cesium.Color.WHITE.withAlpha(0.0),
//設(shè)置粒子圖發(fā)射之后的最小/最大速度(以米/秒為單位),值越大,尾巴飄的越高
minimumSpeed: viewModel.minimumSpeed,
maximumSpeed: viewModel.maximumSpeed,
//startScale endScale,代替scale設(shè)置粒子在生命周期內(nèi)顯示的初始和結(jié)束尺寸,
//也是動態(tài)混合的作用。 值越大,初始時粒子圖的大小越大
startScale: viewModel.startScale,
endScale: viewModel.endScale,
//particleLife:設(shè)置該值將會覆蓋minimumParticleLife和maximumParticleLife
//minimumParticleLife:設(shè)置粒子壽命的可能持續(xù)時間的最小界限(以秒為單位),粒子的實際壽命將隨
//機生成
//maximumParticleLife:設(shè)置粒子壽命的可能持續(xù)時間的最大界限(以秒為單位),粒子的實際壽命將隨
//機生成
//值越大,尾巴越長
minimumParticleLife: viewModel.minimumParticleLife,
maximumParticleLife: viewModel.maximumParticleLife,
//每秒發(fā)射的粒子數(shù)。數(shù)值越大,越濃密
emissionRate: viewModel.emissionRate,
//粒子系統(tǒng)生命周期內(nèi),按照指定周期,爆發(fā)一定數(shù)量的粒子。三個參數(shù)(time,minimum,maximum)
bursts: [
// 爆炸出的粒子的密度
new Cesium.ParticleBurst({
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,
}),
],
//粒子系統(tǒng)會發(fā)射多久粒子,以秒為單位。默認為最大值
lifetime: 16.0,
// 粒子系統(tǒng)的粒子發(fā)射器;包括四種發(fā)射器:圓形、錐體、球體、長方體
emitter: new Cesium.CircleEmitter(2.0),
//modelMatrix: 4x4變換矩陣,將粒子系統(tǒng)從模型轉(zhuǎn)換為世界坐標。
//4x4變換矩陣,用于在粒子系統(tǒng)內(nèi)部坐標系中轉(zhuǎn)換粒子系統(tǒng)發(fā)射器。
emitterModelMatrix: computeEmitterModelMatrix(),
//一組強制回調(diào)?;卣{(diào)被傳遞一個粒子和上次的差值
//每幀都要調(diào)用的回調(diào)函數(shù)來更新粒子。此updateCallback作用是用來改變粒子系統(tǒng)在每一個時間步長的屬
//性,可以強制改變粒子系統(tǒng)的顏色、大小等值。在updateCallback更改gravity重力參數(shù),值越大,煙霧
//離模型越近的地方飄的越高,可能出現(xiàn)負值,此時煙霧會向下方飄。
updateCallback: applyGravity,
})
);
上述通過實例化ParticleSystem,創(chuàng)建了一個卡車后部煙霧效果的粒子系統(tǒng),實例化參數(shù)(如粒子發(fā)射速率,粒子生命周期,粒子顏色、大小、運行速度等)對象控制了單個粒子對象隨時間變化的外觀和行為。如果要想實現(xiàn)不同的粒子效果,還需要對上述參數(shù)進行調(diào)整。上述示例的結(jié)果如下圖所示:

如果在運行過程中要更改粒子系統(tǒng)的屬性, 則需要在ParticleSystem中調(diào)用UpdateCallback更新函數(shù),粒子系統(tǒng)使用modelMatrix和emitterModelMatrix兩個轉(zhuǎn)換矩陣來定位。粒子系統(tǒng)中的每個粒子是如何生成的呢,這就靠下面要講的粒子發(fā)射器了。
3.粒子發(fā)射器
粒子發(fā)射器產(chǎn)生的粒子都有一個位置和類型,存活一段時間后然后消亡。粒子發(fā)射器則控制了粒子產(chǎn)生時的初始位置、速度和方向,并依據(jù)粒子發(fā)射頻率(emissionRate)來決定每秒產(chǎn)生多少粒子,并通過發(fā)射器類型決定粒子的發(fā)射速度和方向。無論是哪種粒子發(fā)射器都繼承了基類ParticleEmitter,Cesium內(nèi)置了如下四種粒子發(fā)射器:
(1)BoxEmitter 盒形發(fā)射器
定義了個盒型,將粒子隨機地放置在盒子里的隨機一個位置,并且具有從盒子中心發(fā)出的初始速度,然后沿著盒子的6個面的法向量向外運動,通過傳遞Cartesian3類型的參數(shù),來定了盒子的長寬高。

(2)CircleEmitter 圓形發(fā)射器
定義了一個圓形,在圓形面中的隨意一個位置發(fā)射具有沿著z矢量的初始速度的粒子,可通過Number類型的參數(shù)控制圓的半徑。Cesium默認的發(fā)射器為圓形發(fā)射器。

(3)ConeEmitter 錐形發(fā)射器
定義了一個的錐體,在錐體頂點產(chǎn)生粒子,并向錐體內(nèi)隨機一個方向運動,參數(shù)可控制圓錐體的角度。

(4)SphereEmitter 球形發(fā)射器
定義了一個球體,在球體內(nèi)隨機產(chǎn)生粒子,并沿著球心向外運動,可通過參數(shù)指定球體的半徑。
4.粒子系統(tǒng)更新回調(diào)函數(shù)
UpdateCallback回調(diào)函數(shù)充當了每個粒子的手動更新程序,它接受兩個參數(shù),一個是粒子本身,另一個是仿真時間步長。每個粒子系統(tǒng)在仿真過程中都會調(diào)用更新回調(diào)函數(shù)來修改粒子的屬性,從而進一步自定義粒子系統(tǒng)。比如對于重力、風或顏色更改等效果的模擬,下面是一個粒子相應重力的示例代碼:
const gravityScratch = new Cesium.Cartesian3();
function applyGravity(p, dt) {
// 計算每個粒子的向上向量(相對地心)
const position = p.position;
Cesium.Cartesian3.normalize(position, gravityScratch);
Cesium.Cartesian3.multiplyByScalar(
gravityScratch,
viewModel.gravity * dt,
gravityScratch
);
p.velocity = Cesium.Cartesian3.add(
p.velocity,
gravityScratch,
p.velocity
);
}
該函數(shù)計算了一個重力方向,并使用重力加速度(-9.8米每秒平方)來改變粒子的速度,然后設(shè)置成粒子的更新回調(diào)函數(shù)。
5.粒子系統(tǒng)位置
有了粒子系統(tǒng)的創(chuàng)建、相關(guān)參數(shù)的位置之后,我們要把顯示的粒子效果具體放在什么位置進行展示呢,這就涉及到粒子系統(tǒng)的定位了。Cesium為我們提供了兩個Matrix4變換矩陣來定位粒子系統(tǒng)以方便開發(fā),當然你也可以設(shè)置一個,而把另一個設(shè)置為單位矩陣。
1)modelMatrix:模型矩陣,將粒子系統(tǒng)從模型轉(zhuǎn)換為世界坐標;
2)emitterModelMatrix:粒子發(fā)射器模型矩陣,在粒子系統(tǒng)的局部坐標系內(nèi)變換粒子發(fā)射器。
我們在創(chuàng)建粒子的時候,粒子發(fā)射器一般都會都會相對于某一個entity或primitive進行定位,用到的就是modelMatrix。如下示例給粒子系統(tǒng)創(chuàng)建了一個模型矩陣,將粒子定位在行駛中的卡車位置中心:
const entity = viewer.entities.add({
availability: new Cesium.TimeIntervalCollection([
new Cesium.TimeInterval({
start: start,
stop: stop,
}),
]),
model: {
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;
viewer.scene.preUpdate.addEventListener(function (scene, time) {
particleSystem.modelMatrix = computeModelMatrix(entity, time);
// Account for any changes to the emitter model matrix.
particleSystem.emitterModelMatrix=computeEmitterModelMatrix();
});
function computeModelMatrix(entity, time) {
return entity.computeModelMatrix(time, new Cesium .Matrix4());
}
而為了使粒子系統(tǒng)定位在卡車的后面,我們需要創(chuàng)建一個在模型坐標系中的平移矩陣,也就是上述示例中的效果,模型矩陣的計算代碼如下:
const emitterModelMatrix = new Cesium.Matrix4();
const translation = new Cesium.Cartesian3();
const rotation = new Cesium.Quaternion();
let hpr = new Cesium.HeadingPitchRoll();
const 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
);
}