UE5 动态天气系统:雨、雪、雾的 Niagara 实现方案

上周有位学员在群里发了一个问题:“老师,我按教程做了个下雨特效,但粒子从天上掉下来时总是穿模到建筑内部,而且雨滴没有碰撞效果,看起来像玻璃碎片。怎么让雨滴碰到地面反弹或者消失?”这个问题其实很典型——很多刚接触 Niagara 的人会把天气特效简单理解为“粒子发射”,忽略了物理交互和视觉真实感的细节。今天我们就从雨、雪、雾三种天气入手,用 Niagara 实现一套可动态切换的天气系统,重点解决粒子碰撞、密度控制、以及雾效的深度融合问题。

一、雨滴的物理碰撞与视觉优化

1.1 基础雨滴粒子设置

首先在 UE5.3 中新建一个 `Niagara System`,选择“来自模板”并选 `Empty`。在 `Emitter Properties` 中设置:

  • Life Cycle:`Emitter Duration` 设为 0(无限循环),`Loop Behavior` 选 `Infinite`
  • Spawn:`Spawn Rate` 设为 1000-2000(根据场景大小调整),`Spawn Group` 用默认
  • 粒子形状用 `Sprite Renderer`,材质选择 `M_RainDrop`(需提前创建,使用半透明 Blend Mode,纹理为水滴状渐变图)。��键参数:

  • Size:`Sprite Size` 设为 `(0.5, 2.0)`,让雨滴呈长条形
  • Color:`Color` 绑定 `LinearColor(0.8, 0.9, 1.0, 0.6)`,透明度 0.6 避免遮挡视线
  • 1.2 碰撞系统的实现

    要让雨滴碰到地面消失或反弹,需要启用 `Collision` 模块。在 Niagara 编辑器中:

    1. 添加 `Collision` 模块到 `Particle Update` 阶段
    2. 设置 `Collision Type` 为 `World Collision`(注意:UE5 中 `World Collision` 会检测静态网格体和地形,但不检测动态物体)
    3. `Collision Mode` 选 `Surface Only`,避免粒子之间互相碰撞
    4. `Friction` 设为 0.3,`Bounce Restitution` 设为 0.1(让雨滴几乎不反弹,直接消失)
    5. 勾选 `Use Velocity for Collision`,让粒子速度影响碰撞方向

    关键技巧:如果发现粒子穿模到建筑内部,检查 `Collision Channel` 是否包含 `WorldStatic`。在项目设置中,`Collision` → `Preset` 里确保 `WorldStatic` 对 `Particle` 通道是 `Block`。另外,`Collision Radius` 设为 0.1 可减少粒子在边缘的抖动。

    1.3 视觉增强:风与动态模糊

    雨滴需要随风飘动,在 `Particle Update` 中添加 `Force` 模块:

  • `Force` 的 `X` 和 `Y` 轴绑定 `WindDirection` 变量(可在 `User Parameters` 中暴露)
  • `Wind Speed` 设为 100-300 cm/s,方向用 `Vector(1,0,0)` 模拟北风
  • 为了让雨滴有动态模糊效果,在 `Sprite Renderer` 的 `Material Parameters` 中,将 `Particle Direction` 传入材质的 `UV` 偏移量。材质蓝图里用 `Particle Relative Time` 乘以速度向量,让纹理沿粒子运动方向拉伸。

    雨滴碰撞效果示意图

    二、雪的堆积与密度控制

    2.1 粒子发射参数优化

    雪的粒子比雨滴更轻、更慢。新建一个 Niagara System,参数调整如下:

  • Spawn Rate:500-1000(雪不需要太多粒子,否则性能下降)
  • Initial Velocity:`Vector(-50, -50, -200)`,X/Y 方向随机偏移,模拟飘落
  • Life Time:`Uniform(5,15)` 秒,让雪在落地前有足够时间飘动
  • Size:`Uniform(0.3, 0.8)`,圆形 Sprite,材质用 `M_SnowFlake`(六边形渐变纹理)
  • 2.2 堆积效果的实现

    雪落在物体表面需要形成堆积层。这里用 `Collision` + `Particle ID` 实现:

    1. 在 `Emitter Update` 中添加 `Spawn Per Unit`,设置 `Spawn Rate` 为 0(禁用自动生成),改用 `Collision` 事件触发生成堆积粒子
    2. 添加 `Event Handler`,监听 `Collision Event`,当粒子碰撞时,在碰撞位置生成一个���的 `Snow Pile` 粒子
    3. `Snow Pile` 粒子的 `Life Time` 设为 30-60 秒,`Size` 逐渐增大(用 `Particle Age` 控制缩放),模拟积雪

    注意:堆积粒子需要禁用 `Collision`,否则会继续触发事件导致无限循环。另外,`Snow Pile` 粒子的 `Sprite Renderer` 用 `Unlit` 材质,颜色设为 `(0.9,0.95,1.0)`,透明度 0.8。

    2.3 密度控制脚本

    为了在不同区域控制雪的密度,用 `User Parameters` 暴露 `Snow Density`(Float,0-1)。在 `Spawn` 阶段用 `Spawn Burst Instantaneous`,数量乘以 `Snow Density`。另外,在 `Particle Update` 中添加 `Scale Color` 模块,根据密度调整透明度:

    Color = Lerp(LinearColor(0,0,0,0), LinearColor(1,1,1,1), Snow Density)
    

    这样密度为 0 时雪完全透明,达到“无雪”效果。

    雪堆积效果示意图

    三、雾效的深度与动态控制

    3.1 基于高度场的雾粒子

    雾不能用传统 Sprite 粒子,因为需要体积感。推荐用 `Mesh Renderer` 配合球体网格:

    1. 在 Niagara 中创建 `Emitter`,`Renderer` 选 `Mesh Renderer`
    2. `Mesh` 选择 `Sphere`(引擎自带),`Scale` 设为 `Uniform(100,300)` cm
    3. `Material` 用 `M_FogVolume`,材质设置为 `Unlit`,`Blend Mode` 为 `Additive`
    4. 粒子数量:100-300 个即可,太多会卡

    关键参数:

  • Spawn Rate:10-30 个/秒
  • Life Time:`Uniform(5,10)` 秒
  • Initial Position:`Cylinder Location`,半径 5000 cm,高度 200-500 cm(模拟低空雾)
  • Velocity:`Vector(10,10,0)`,让雾缓慢漂移
  • 3.2 密度与高度的动态映射

    雾的密度应该随高度变化:地面浓,高空稀。在 `Particle Update` 中添加 `Scale Color` 模块,用 `Particle Position Z` 计算:

    Height = Particle Position Z - Ground Z
    Density = 1 - saturate(Height / Max Height)
    Color = LinearColor(0.8, 0.85, 0.9, Density * 0.5)
    

    `Max Height` 设为 500 cm(雾顶高度),`Ground Z` 用 `User Parameter` 暴露,方便调整。

    3.3 与场景光照的融合

    雾的颜色需要匹配环境光。在 `User Parameters` 中添加 `Fog Color`(LinearColor),从场景的 `Directional Light` 颜色采样。在材质中,用 `Fog Color` 乘以 `Particle Color`,实现动态调色。

    进阶技巧:用 `Noise` 模块让雾粒子位置产生随机扰动,模拟风的影响。在 `Particle Update` 中添加 `Noise`,`Noise Mode` 选 `Position`,`Frequency` 设为 0.1,`Amplitude` 设为 50 cm,让雾产生流动感。

    雾效体积感示意图

    四、系统集成与动态切换

    4.1 天气状态蓝图控制

    在蓝图中创建一个 `Weather Manager` Actor,包含三个 Niagara 组件(分别对应雨、雪、雾)。暴露三个布尔变量:`bRain`、`bSnow`、`bFog`,在 `Tick` 中根据状态启用/禁用组件:

    // 伪代码
    if (bRain) RainComponent->Activate();
    else RainComponent->Deactivate();
    

    注意:`Activate` 会重置粒子系统,建议用 `SetVisibility` 控制显示/隐藏,避免性能开销。

    4.2 过渡动画

    从雨切换到雪时,需要平滑过渡。在 Niagara 中,用 `User Parameters` 暴露 `Transition Time`(Float),在 `Spawn` 阶段用 `Lerp` 将粒子数量从当前值过渡到目标值。例如:

    Spawn Rate = Lerp(CurrentRate, TargetRate, Transition Time / Total Transition Duration)
    

    `Total Transition Duration` 设为 2-3 秒,让天气变化自然。

    五、性能优化建议

    1. LOD 控制:对远处的粒子降低 `Spawn Rate` 和 `Size`。在 Niagara 中,用 `Camera Distance` 条件判断,距离 > 5000 cm 时粒子数量减半。
    2. 粒子池复用:用 `Particle Pool` 模块,避免频繁分配/释放内存。
    3. 材质复杂度:雨滴和雪的材质用 `Simple` 材质域,雾的材质用 `Surface`,避免使用 `World Position Offset` 等重计算。

    总结与进阶建议

    这套天气系统已经解决了粒子碰撞、堆积和雾效体积感的核心问题。但如果你想做更真实的效果,可以进一步探索:

  • 雨滴涟漪:在碰撞位置生成环形粒子,模拟水花
  • 雪融化:用 `Particle Age` 控制堆积粒子逐渐缩小并消失
  • 雾的密度场:结合 `Environment Query System` 让雾根据地形高度变化
  • 建议学员先实现基础版本,再逐步添加细节。如果遇到粒子性能问题,优先检查 `Spawn Rate` 和 `Collision` 的 `Trace Channel` 设置。

    常见问题 FAQ

    Q1:粒子碰撞时总是穿透地面,怎么解决?
    A:检查 `Collision Channel` 是否包含 `WorldStatic`,并在项目设置中确认 `Particle` 通道对 `WorldStatic` 是 `Block`。另外,`Collision Radius` 设置过小(<0.05)会导致穿透,建议设为 0.1-0.2。

    Q2:雪的堆积粒子无限生成,导致场景卡死?
    A:堆积粒子必须禁用 `Collision`,否则会反复触发碰撞事件。在 `Snow Pile` 的 `Emitter Properties` 中,将 `Collision` 模块移除或设为 `Disabled`。

    Q3:雾的粒子看起来像一团团球体,没有体积感?
    A:尝试用 `Noise` 模块扰动粒子位置,同时将 `Mesh Scale` 的随机范围扩大(如 100-500 cm)。材质中增加 `Opacity` 的随机性,让粒子边缘淡出。

    Q4:天气切换时有明显的粒子闪烁?
    A:这���由于 `Activate`/`Deactivate` 重置了粒子系统。改用 `SetVisibility` 控制组件显示,并在 `User Parameters` 中用 `Lerp` 过渡粒子数量。

    Q5:雨滴在高速移动时出现撕裂?
    A:在 `Sprite Renderer` 中启用 `Sort Priority`,设置为 `-10`(让雨滴优先渲染)。同时增大 `Sub Image` 的 `Blend` 值,或改用 `Triangle` 网格代替 Sprite。

    如果你在实现过程中遇到具体问题,欢迎在评论区留言,我会挑选典型问题在后续文章中详细解答。

    声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。