原文链接及内容

效果如下视频所示:

示例代码如下:

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
const viewer = new Cesium.Viewer("cesiumContainer", {
geocoder: false,
homeButton: false,
sceneModePicker: false,
navigationHelpButton: false,
navigationInstructionsInitiallyVisible: false,
animation: false,
timeline: false,
fullscreenButton: false,
skyBox: false,
shouldAnimate: true,
baseLayerPicker: false,
shadows: true,
terrain: Cesium.Terrain.fromWorldTerrain(),
});
viewer.cesiumWidget.creditContainer.style.display = "none";

const scene = viewer.scene;
scene.globe.depthTestAgainstTerrain = true;

const resetCameraFunction = function () {
scene.camera.setView({
destination: new Cesium.Cartesian3(
277096.634865404,
5647834.481964232,
2985563.7039122293,
),
orientation: {
heading: 4.731089976107251,
pitch: -0.32003481981370063,
},
});
};
resetCameraFunction();

// 定义雪粒子的行为,使用粒子系统模拟下雪效果。
const snowParticleSize = 12.0;//雪粒子基础尺寸
const snowRadius = 100000.0;//雪花的活动范围,即相机周围的球形区域。
//minimumSnowImageSize与maximumSnowImageSize:雪花图像的最小和最大尺寸,控制视觉变化。
const minimumSnowImageSize = new Cesium.Cartesian2(
snowParticleSize,
snowParticleSize,
);
const maximumSnowImageSize = new Cesium.Cartesian2(
snowParticleSize * 2.0,
snowParticleSize * 2.0,
);

let snowGravityScratch = new Cesium.Cartesian3();
const snowUpdate = function (particle, dt) {
/**
* 通过 normalize 和 multiplyByScalar
* 计算一个随机向下加速度(-30 到 -300),模拟雪花自然下落。
*/
snowGravityScratch = Cesium.Cartesian3.normalize(
particle.position,
snowGravityScratch,
);
Cesium.Cartesian3.multiplyByScalar(
snowGravityScratch,
Cesium.Math.randomBetween(-30.0, -300.0),
snowGravityScratch,
);
//位置更新
particle.velocity = Cesium.Cartesian3.add(
particle.velocity,
snowGravityScratch,
particle.velocity,
);
/**
* 根据粒子与相机的距离 distance,调整透明度:
* - 若超出 snowRadius,透明度为 0(不可见)。
* - 若在范围内,透明度随距离增加而减小,营造远近效果。
*/
const distance = Cesium.Cartesian3.distance(
scene.camera.position,
particle.position,
);
if (distance > snowRadius) {
particle.endColor.alpha = 0.0;
} else {
particle.endColor.alpha = 1.0 / (distance / snowRadius + 0.1);
}
};

// 雨
const rainParticleSize = 15.0;//雨滴的基础尺寸(15.0)
const rainRadius = 100000.0;//雨滴的活动范围(100,000 单位),同上,即相机周围的球形区域。
//雨滴的图像尺寸,高度是宽度的两倍,模拟细长雨滴。
const rainImageSize = new Cesium.Cartesian2(
rainParticleSize,
rainParticleSize * 2.0,
);
let rainGravityScratch = new Cesium.Cartesian3();
const rainUpdate = function (particle, dt) {
//固定向下加速度(-1050),雨滴下落速度比雪花快。
rainGravityScratch = Cesium.Cartesian3.normalize(
particle.position,
rainGravityScratch,
);
rainGravityScratch = Cesium.Cartesian3.multiplyByScalar(
rainGravityScratch,
-1050.0,
rainGravityScratch,
);

//位置更新:直接更新粒子位置,而非速度,模拟雨的直线下落。
particle.position = Cesium.Cartesian3.add(
particle.position,
rainGravityScratch,
particle.position,
);

const distance = Cesium.Cartesian3.distance(
scene.camera.position,
particle.position,
);
//透明度调整与雪类似,根据与相机的距离调整透明度,超出范围则不可见。
if (distance > rainRadius) {
particle.endColor.alpha = 0.0;
} else {
particle.endColor.alpha =
Cesium.Color.BLUE.alpha / (distance / rainRadius + 0.1);
}
};

// button
Sandcastle.addToolbarButton("相机重置", resetCameraFunction);

function startSnow() {
scene.primitives.removeAll();
scene.primitives.add(
new Cesium.ParticleSystem({
modelMatrix: new Cesium.Matrix4.fromTranslation(scene.camera.position),//粒子系统跟随相机位置。
minimumSpeed: -1.0,
maximumSpeed: 0.0,
lifetime: 15.0,
emitter: new Cesium.SphereEmitter(snowRadius),//使用球形发射器,范围为 snowRadius。
// startScale 和 endScale: 雪花从 0.5 倍大小放大到 1 倍。
startScale: 0.5,
endScale: 1.0,
image: "../SampleData/snowflake_particle.png",
emissionRate: 7000.0,//每秒发射 7000 个粒子。
startColor: Cesium.Color.WHITE.withAlpha(0.0),
endColor: Cesium.Color.WHITE.withAlpha(1.0),
minimumImageSize: minimumSnowImageSize,
maximumImageSize: maximumSnowImageSize,
updateCallback: snowUpdate,//更新粒子行为
}),
);

//调整天空大气,修改色调、饱和度、亮度和雾效,营造下雪的阴冷感觉。
scene.skyAtmosphere.hueShift = -0.8;
scene.skyAtmosphere.saturationShift = -0.7;
scene.skyAtmosphere.brightnessShift = -0.33;
scene.fog.density = 0.001;
scene.fog.minimumBrightness = 0.8;
}

// drop down
const options = [
{
text: "雪",
onselect: startSnow,
},
{
text: "雨",
onselect: function () {
scene.primitives.removeAll();
scene.primitives.add(
new Cesium.ParticleSystem({
modelMatrix: new Cesium.Matrix4.fromTranslation(scene.camera.position),
speed: -1.0,
lifetime: 15.0,
emitter: new Cesium.SphereEmitter(rainRadius),
//startScale 和 endScale: 雨滴从 1 倍缩小到 0。
startScale: 1.0,
endScale: 0.0,
image: "../SampleData/circular_particle.png",//圆形粒子纹理
emissionRate: 9000.0,//每秒 9000 个粒子。
startColor: new Cesium.Color(0.27, 0.5, 0.7, 0.0),
endColor: new Cesium.Color(0.27, 0.5, 0.7, 0.98),
imageSize: rainImageSize,
updateCallback: rainUpdate,
}),
);

///调整天空大气,修改色调、饱和度、亮度和雾效,营造下雨的阴沉氛围。
scene.skyAtmosphere.hueShift = -0.97;
scene.skyAtmosphere.saturationShift = 0.25;
scene.skyAtmosphere.brightnessShift = -0.4;
scene.fog.density = 0.00025;
scene.fog.minimumBrightness = 0.01;
},
},
];
Sandcastle.addToolbarMenu(options);
startSnow();