Cesium天气特效进阶:手把手教你魔改GLSL代码,自定义暴风雪和雷暴雨效果
当我们需要在数字地球场景中模拟极端天气时,基础雨雪效果往往难以满足需求。想象一下游戏中的暴风雪场景:雪花不再是匀速下落,而是在狂风中旋转飞舞;或是雷暴雨天气中,闪电划破天际,雨滴在强风中倾斜坠落。这些效果都可以通过深度定制GLSL代码实现。
1. 理解Cesium的后期处理管线
Cesium的PostProcessStage允许我们在场景渲染完成后添加全屏特效。与粒子系统不同,这种方式不受视角变化影响,性能开销也更低。核心原理是:
- 场景先被渲染到一张纹理(colorTexture)
- 我们的GLSL代码对这张纹理进行处理
- 处理后的结果与原始场景混合
// 创建后期处理阶段的基本结构 const customWeather = new Cesium.PostProcessStage({ name: 'czm_custom_weather', fragmentShader: weatherShader // 我们的GLSL代码 }); viewer.scene.postProcessStages.add(customWeather);2. 暴风雪效果深度定制
基础雪效只是简单的白点下落,真正的暴风雪需要多个层次的动态效果:
2.1 风速与粒子运动
暴风雪的核心特征是强风影响下的雪花运动。我们需要在GLSL中实现:
// 暴风雪运动算法 vec2 windForce = vec2( sin(time * 0.5) * 3.0, // x轴方向风力 cos(time * 0.3) * 1.5 // y轴方向风力 ); uv.x += sin(uv.y * 10.0 + time * 2.0) * 0.1 * windForce.x; uv.y += (time * speed) + windForce.y;关键参数调节表:
| 参数 | 作用 | 推荐值范围 | 效果影响 |
|---|---|---|---|
| windForce.x | 水平风力 | 0.5-5.0 | 值越大雪花横向移动越剧烈 |
| windForce.y | 垂直风力 | 0.1-2.0 | 负值会使雪花向上飞 |
| time系数 | 风力变化频率 | 0.1-1.0 | 决定风力变化的快慢 |
2.2 多层雪花系统
真实的暴风雪包含不同大小的雪花:
float stormIntensity = 0.0; stormIntensity += snow(uv, 30.0) * 0.3; // 远处大雪花 stormIntensity += snow(uv, 15.0) * 0.7; // 中等雪花 stormIntensity += snow(uv, 5.0) * 1.0; // 近处小雪花调试技巧:在开发过程中,可以先用纯色背景测试效果,避免场景复杂度的干扰。使用
gl_FragColor = vec4(vec3(stormIntensity), 1.0);直接查看雪花分布。
3. 雷暴雨效果实现
雷暴雨需要结合雨效、闪电和大气变暗三种效果。
3.1 动态雨线算法
// 改进的雨线算法 float rainLine(vec2 uv, float speed, float tilt) { float rain = 0.0; uv.x += uv.y * tilt; // 雨线倾斜度 vec2 rainUV = uv * vec2(1.0, 0.3); rainUV.y += time * speed; vec2 grid = fract(rainUV * 100.0); float random = fract(sin(dot(floor(rainUV * 100.0), vec2(12.9898,78.233))) * 43758.5453); rain = smoothstep(0.4, 0.5, grid.x) * smoothstep(0.7 + random*0.2, 0.3, grid.y); return rain * (0.5 + random*0.5); }3.2 闪电效果实现
闪电需要随机触发和光晕效果:
// 闪电算法 float lightning = 0.0; float lightningChance = fract(sin(time * 0.1) * 1000.0); if(lightningChance > 0.995) { float lightningPos = fract(sin(time * 0.5) * 500.0) * 2.0 - 1.0; float lightningDist = abs(uv.x - lightningPos * 0.5); lightning = 0.1 / (lightningDist + 0.05) * smoothstep(0.0, 0.5, 1.0 - fract(time * 2.0)); }4. 高级混合与调试技巧
4.1 天气效果混合
当需要同时表现多种天气时,混合策略至关重要:
// 最终效果混合 vec3 weatherEffect = vec3(0.0); // 基础权重计算 float snowWeight = clamp(stormIntensity * 2.0, 0.0, 1.0); float rainWeight = clamp(rainIntensity * 3.0, 0.0, 1.0); // 混合雨雪(暴风雪时减少雨效) weatherEffect += snowColor * snowWeight * (1.0 - rainWeight * 0.5); weatherEffect += rainColor * rainWeight * (1.0 - snowWeight * 0.3); // 添加闪电 weatherEffect += lightning * vec3(0.9, 0.95, 1.0); // 场景混合 vec4 sceneColor = texture2D(colorTexture, v_textureCoordinates); vec4 finalColor = mix(sceneColor, vec4(weatherEffect, 1.0), overallIntensity);4.2 性能优化策略
复杂天气效果可能影响性能,这些优化方法很实用:
降低采样精度:对全屏效果,可以降低计算精度
vec2 uv = floor(gl_FragCoord.xy * 0.5) * 2.0 / czm_viewport.zw;距离衰减:根据视距减少细节
float depth = czm_unpackDepth(texture2D(depthTexture, v_textureCoordinates)); float detailLevel = 1.0 - smoothstep(1000.0, 10000.0, depth);时间分片:非关键效果可以隔帧更新
float updateFactor = mod(czm_frameNumber, 2.0) < 1.0 ? 1.0 : 0.0;
5. 动态天气过渡与场景互动
真正的专业级效果需要考虑天气变化过程和环境互动:
5.1 平滑天气过渡
// 统一的时间控制器 uniform float weatherTransition; // 0.0-1.0 // 在效果计算中使用过渡参数 float currentSnow = snowEffect * smoothstep(0.0, 0.3, weatherTransition); float currentRain = rainEffect * smoothstep(0.3, 1.0, weatherTransition);5.2 地形互动效果
让天气与地形产生互动可以大幅提升真实感:
// 获取场景深度信息 float depth = czm_unpackDepth(texture2D(depthTexture, v_textureCoordinates)); vec3 worldPos = czm_windowToEyeCoordinates(gl_FragCoord.xyz, depth); // 积雪累积效果 if(depth < 0.999) { float groundSnow = smoothstep(0.2, 0.5, worldPos.y) * smoothstep(0.0, 0.3, weatherTransition); finalColor.rgb = mix(finalColor.rgb, vec3(0.9), groundSnow * 0.3); }实现这些效果后,记得在JavaScript端添加控制接口:
// 天气过渡控制 let transition = 0; function updateWeather() { transition = Cesium.Math.clamp(transition + 0.01, 0, 1); customWeather.uniforms.weatherTransition = transition; if(transition < 1) { requestAnimationFrame(updateWeather); } }GLSL天气特效的开发是一个不断迭代的过程,最好的学习方式就是修改参数观察变化。从简单的雪效开始,逐步添加风力、多层粒子、环境互动等特性,最终可以创造出电影级的天气视觉效果。