cesium(笔记-4)

运行环境 | cesium 1.58 | webstorm 2018.2.2 | windows 10 pro

粒子系统是一种图形技术,可以模拟复杂的物理效果。粒子系统是小图像的集合,当它们一起观看时,会形成一个更复杂的“模糊”物体,如火、烟、天气或烟花fireworkds。通过使用诸如初始位置、速度和寿命等属性指定单个粒子的行为,可以控制这些复杂的效果。

粒子系统效应在电影和电子游戏中很常见。例如,为了表示飞机的损坏,技术艺术家可以使用粒子系统来表示飞机引擎上的爆炸,然后渲染不同的粒子系统,表示飞机坠毁时的烟雾轨迹。

Particle system basics 粒子系统基础

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var particleSystem = viewer.scene.primitives.add(new Cesium.ParticleSystem({
image : '../../SampleData/smoke.png',
imageSize : new Cesium.Cartesian2(20, 20),
startScale : 1.0,
endScale : 4.0,
particleLife : 1.0,
speed : 5.0,
emitter : new Cesium.CircleEmitter(0.5),
emissionRate : 5.0,
modelMatrix : entity.computeModelMatrix(viewer.clock.startTime, new Cesium.Matrix4()),
lifetime : 16.0
}));
```

## Emitters 发射器
当粒子诞生时,其初始位置和速度矢量由ParticleEmitter控制。发射器将每秒生成一些粒子,由emissionRate参数指定,根据发射器类型用随机速度初始化。
### BoxEmitter 盒形发射器

var particleSystem = scene.primitives.add(new Cesium.ParticleSystem({
image : ‘../../SampleData/smoke.png’,
color: Cesium.Color.MAGENTA,
emissionRate: 5.0,
emitter: new Cesium.BoxEmitter(new Cesium.Cartesian3(5.0, 5.0, 5.0)),
imageSize : new Cesium.Cartesian2(25.0, 25.0),
modelMatrix : entity.computeModelMatrix(viewer.clock.startTime, new Cesium.Matrix4()),
lifetime : 16.0
}));

1
2
### CircleEmitter 圆形发射器
BoxEmitter在一个盒子内随机取样的位置初始化粒子,从六个盒子表面中的一个引导出来.

1
2
3
4
5
6
7
8
9
10
11
12
13

### CircleEmitter 圆形发射器
CircleEmitter在发射器上轴线方向上的圆形内的随机采样位置初始化粒子。
```js
var particleSystem = scene.primitives.add(new Cesium.ParticleSystem({
image : '../../SampleData/smoke.png',
color: Cesium.Color.MAGENTA,
emissionRate: 5.0,
emitter: new Cesium.CircleEmitter(5.0),
imageSize : new Cesium.Cartesian2(25.0, 25.0),
modelMatrix : entity.computeModelMatrix(viewer.clock.startTime, new Cesium.Matrix4()),
lifetime : 16.0
}));

ConeEmitter 锥形发射器

ConeEmitter在圆锥体的顶端初始化粒子,并以随机的角度引导它们离开圆锥体。

1
2
3
4
5
6
7
8
9
var particleSystem = scene.primitives.add(new Cesium.ParticleSystem({
image : '../../SampleData/smoke.png',
color: Cesium.Color.MAGENTA,
emissionRate: 5.0,
emitter: new Cesium.ConeEmitter(Cesium.Math.toRadians(30.0)),
imageSize : new Cesium.Cartesian2(25.0, 25.0),
modelMatrix : entity.computeModelMatrix(viewer.clock.startTime, new Cesium.Matrix4()),
lifetime : 16.0
}));

SphereEmitter 球形发射器

SphereEmitter在球体内随机取样的位置初始化粒子,并将它们从球体中心向外引导。

1
2
3
4
5
6
7
8
9
var particleSystem = scene.primitives.add(new Cesium.ParticleSystem({
image : '../../SampleData/smoke.png',
color: Cesium.Color.MAGENTA,
emissionRate: 5.0,
emitter: new Cesium.SphereEmitter(5.0),
imageSize : new Cesium.Cartesian2(25.0, 25.0),
modelMatrix : entity.computeModelMatrix(viewer.clock.startTime, new Cesium.Matrix4()),
lifetime : 16.0
}));

Particle emission rate 粒子发射率

emissionRate控制每秒发射多少粒子,这会改变系统中粒子的密度。 指定一组突burst以在指定时间发射粒子burst(如上面的动画所示)。这会增加粒子系统的多样性或爆炸性。

1
2
3
4
5
bursts : [
new Cesium.ParticleBurst({time : 5.0, minimum : 300, maximum : 500}),
new Cesium.ParticleBurst({time : 10.0, minimum : 50, maximum : 100}),
new Cesium.ParticleBurst({time : 15.0, minimum : 200, maximum : 300})
]

Life of the particle and life of the system 粒子寿命和系统寿命

1
2
3
4
5
lifetime : 16.0,
loop: false
//要随机化每个粒子的输出,使用变量minimumParticleLife和maximumArticleLife。
minimumParticleLife: 5.0,
maximumParticleLife: 10.0

Styling particles 样式化粒子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//Color 颜色
startColor : Cesium.Color.LIGHTSEAGREEN.withAlpha(0.7),
endColor : Cesium.Color.WHITE.withAlpha(0.0),

//Size 大小
minimumImageSize : new Cesium.Cartesian2(30.0, 30.0),
maximumImageSize : new Cesium.Cartesian2(60.0, 60.0)

//可以通过startScale和endscale属性在其生命周期中进行调整,以使粒子随时间增长或收缩。
startScale: 1.0,
endScale: 4.0

//速度由speed或minimumSpeed和maximumSpeed控制。
minimumSpeed: 5.0,
maximumSpeed: 10.0

通过应用更新函数,可以进一步自定义粒子系统。

对于重力、风或颜色更改等效果,它充当每个粒子的手动更新程序。
项目系统有一个updateCallback,它在模拟过程中修改粒子的属性。此函数采用粒子和模拟时间步骤。大多数基于物理的效果将修改速度矢量以改变方向或速度。下面是一个让粒子对重力作出反应的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var gravityVector = new Cesium.Cartesian3();
var gravity = -(9.8 * 9.8);
function applyGravity(p, dt) {
// Compute a local up vector for each particle in geocentric space.
var position = p.position;

Cesium.Cartesian3.normalize(position, gravityVector);
Cesium.Cartesian3.multiplyByScalar(gravityVector, gravity * dt, gravityVector);

p.velocity = Cesium.Cartesian3.add(p.velocity, gravityVector, p.velocity);
}

//该函数计算重力矢量,并使用重力加速度来改变粒子的速度。 将重力设置为粒子系统的updateFunction:
updateCallback : applyGravity

Positioning 定位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*
modelMatrix:将粒子系统从模型转换为世界坐标。
emitterModelMatrix:在粒子系统的局部坐标系中变换粒子系统发射器。
*/
var entity = viewer.entities.add({
model : {
uri : '../../SampleData/models/CesiumMilkTruck/CesiumMilkTruck-kmc.glb'
},
position : Cesium.Cartesian3.fromDegrees(-75.15787310614596, 39.97862668312678)
});
viewer.trackedEntity = entity;

modelMatrix: entity.computeModelMatrix(time, new Cesium.Matrix4())


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 particleSystem = viewer.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 : 1.0,
endScale : 4.0,

particleLife : 1.0,

minimumSpeed : 1.0,
maximumSpeed : 4.0

imageSize : new Cesium.Cartesian2(25, 25),
emissionRate : 5.0,
lifetime : 16.0,

modelMatrix : entity.computeModelMatrix(viewer.clock.startTime, new Cesium.Matrix4())
emitterModelMatrix : computeEmitterModelMatrix()
}));

完整示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
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;

//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 : {
uri : '../SampleData/models/CesiumMilkTruck/CesiumMilkTruck-kmc.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,

bursts : [
// these burst will occasionally sync to create a multicolored effect
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})
],

lifetime : 16.0,

emitter : new Cesium.CircleEmitter(2.0),

emitterModelMatrix : computeEmitterModelMatrix(),

updateCallback : applyGravity
}));

var gravityScratch = new Cesium.Cartesian3();

function applyGravity(p, dt) {
// We need to compute a local up vector for each particle in geocentric space.
var 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);
}

viewer.scene.preUpdate.addEventListener(function(scene, time) {
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) {
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);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
<style>
@import url(../templates/bucket.css);

#toolbar {
background: rgba(42, 42, 42, 0.8);
padding: 4px;
border-radius: 4px;
}

#toolbar input {
vertical-align: middle;
padding-top: 2px;
padding-bottom: 2px;
}

#toolbar .header {
font-weight: bold;
}
</style>
<div id="cesiumContainer" class="fullSize"></div>
<div id="loadingOverlay"><h1>Loading...</h1></div>
<div id="toolbar">
<table>
<tbody>
<tr>
<td>Rate</td>
<td>
<input type="range" min="0.0" max="100.0" step="1" data-bind="value: emissionRate, valueUpdate: 'input'">
<input type="text" size="5" data-bind="value: emissionRate">
</td>
</tr>

<tr>
<td>Size</td>
<td>
<input type="range" min="2" max="60.0" step="1" data-bind="value: particleSize, valueUpdate: 'input'">
<input type="text" size="5" data-bind="value: particleSize">
</td>
</tr>

<tr>
<td>Min Life</td>
<td>
<input type="range" min="0.1" max="30.0" step="1" data-bind="value: minimumParticleLife, valueUpdate: 'input'">
<input type="text" size="5" data-bind="value: minimumParticleLife">
</td>
</tr>

<tr>
<td>Max Life</td>
<td>
<input type="range" min="0.1" max="30.0" step="1" data-bind="value: maximumParticleLife, valueUpdate: 'input'">
<input type="text" size="5" data-bind="value: maximumParticleLife">
</td>
</tr>

<tr>
<td>Min Speed</td>
<td>
<input type="range" min="0.0" max="30.0" step="1" data-bind="value: minimumSpeed, valueUpdate: 'input'">
<input type="text" size="5" data-bind="value: minimumSpeed">
</td>
</tr>

<tr>
<td>Max Speed</td>
<td>
<input type="range" min="0.0" max="30.0" step="1" data-bind="value: maximumSpeed, valueUpdate: 'input'">
<input type="text" size="5" data-bind="value: maximumSpeed">
</td>
</tr>

<tr>
<td>Start Scale</td>
<td>
<input type="range" min="0.0" max="10.0" step="1" data-bind="value: startScale, valueUpdate: 'input'">
<input type="text" size="5" data-bind="value: startScale">
</td>
</tr>

<tr>
<td>End Scale</td>
<td>
<input type="range" min="0.0" max="10.0" step="1" data-bind="value: endScale, valueUpdate: 'input'">
<input type="text" size="5" data-bind="value: endScale">
</td>
</tr>

<tr>
<td>Gravity</td>
<td>
<input type="range" min="-20.0" max="20.0" step="1" data-bind="value: gravity, valueUpdate: 'input'">
<input type="text" size="5" data-bind="value: gravity">
</td>
</tr>
</tbody>
</table>
</div>
本文结束感谢您的阅读,本文原创–支持原创
顺便打点赏吧~ 有问题请联系我--strivedeer@163.com