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

上周有位学员在群里发了一个项目截图:他花了两周用蓝图堆出来的暴雨场景,粒子数量超过10万,帧率直接掉到18fps。他问我:“老师,是不是UE5的Niagara不适合做大规模天气特效?” 其实问题不在Niagara,而在于他用了CPU模拟,并且没有做LOD和剔除优化。今天我们就从零开始,用Niagara实现一套高性能的动态天气系统,涵盖雨、雪、雾三种核心效果,并加入时间轴控制,让你能在游戏或影视场景中一键切换天气。

一、核心架构:用Niagara Emitter与Parameter Map构建模块化系统

在动手前,先明确一个原则:不要在一个Emitter里塞满所有逻辑。我们需要为雨、雪、雾分别创建独立的Emitter,然后用一个主Niagara System(版本:UE 5.3+)通过User Exposed Parameters控制它们的开启、密度和物理行为。

1.1 创建基础雨效Emitter

打开Niagara编辑器,新建一个Emitter模板,命名为`Rain_Heavy`。关键参数设置如下:

  • Spawn Rate:10000(每秒生成粒子数,可根据性能调整)
  • Particle Life:1.5秒(模拟雨滴下落时间)
  • Initial Velocity:Z轴设为-1500(模拟重力加速度,单位cm/s)
  • Size:X=0.2, Y=0.2, Z=1.5(长条形模拟雨丝)
  • 重点在于Collision模块。我们需要让雨滴碰到地面时产生飞溅效果(用Sprite代替复杂模型)。在Emitter Update中添加`Collision`,勾选`Use Collision`,`Collision Mode`选`Surface Only`,`Friction`设为0.3,`Bounce Restitution`设为0.1。然后在Particle Update中添加`Spawn Burst Instantaneous`,触发条件设为`Collision Normal Z < -0.8`,生成2-3个飞溅粒子,生命周期0.3秒,初始速度为随机方向(-200到200)。

    雨滴碰撞飞溅效果

    1.2 雪花旋转与堆积逻辑

    雪效的难点在于旋转飘落地面堆积。新建一个`Snow_Flurry` Emitter,核心参数:

  • Spawn Rate:5000(雪密度比雨低,避免性能消耗)
  • Particle Life:8秒(模拟缓慢下落)
  • Initial Velocity:Z=-200,X和Y随机范围-50到50(模拟风)
  • Size:均匀0.8(圆形Sprite)
  • 为了实现旋转,在Particle Update中添加`Add Velocity`模块,用一个`Sine`函数驱动水平方向的偏移:`Velocity.X += sin(TotalTime 2 + RandomFloat) 20`。这样每片雪花都会以不同的相位左右摇摆,模拟真实飘落。

    雪花旋转飘落轨迹

    地面堆积则需要粒子存活检测。在Particle Update中添加`Scalability`模块,设置`LOD Distance`:当粒子距离地面Z<10cm时,将`Particle Life`设为无穷大(9999秒),并冻结其位置。同时添加一个`Mesh Renderer`,用简单的平面网格替代Sprite,避免视觉穿模。注意:堆积粒子总数建议限制在1000以内,否则会极大消耗GPU。

    1.3 雾效的体积感与动态扩散

    雾效不能用传统Sprite粒子,否则会像“棉花糖”。我们需要Volume Renderer。新建`Fog_Dynamic` Emitter,使用`Grid 2D`或`Grid 3D`渲染器(推荐Grid 3D,性能更好)。关键参数:

  • Grid Resolution:32x32x16(分辨率越高越细腻,但性能开销大)
  • Density:0.3(基础密度)
  • Noise Texture:使用引擎自带的`T_Noise`,Scale设为200,让雾的分布有自然起伏
  • Animation:在Particle Update中添加`Noise Field`模块,用`Particles.Position`的X轴偏移驱动密度变化,模拟雾的流动
  • 为了与雨雪交互,雾的高度衰减很重要。在`Density`参数上乘一个`Linear Interpolate`,根据粒子Z轴位置计算:`Lerp(0.8, 0.1, (Particles.Position.Z – 0) / 500)`。这样地面雾浓,高空雾淡,符合物理规律。

    雾效体积渲染与高度衰减

    二、动态控制:用蓝图或C++实时切换天气

    有了三个Emitter后,我们需要一个主控制器。在Niagara System的`User Exposed Parameters`中添加三个`Boolean`变量:`EnableRain`、`EnableSnow`、`EnableFog`。然后在每个Emitter的`Spawn Rate`模块中,用`If`节点判断对应的Boolean值,为true时使用正常Spawn Rate,为false时设为0。

    蓝图示例(在关卡蓝图中):

    // 获取Niagara组件
    UNiagaraComponent* WeatherSystem = Cast(GetOwner()->GetComponentByClass(UNiagaraComponent::StaticClass()));

    // 设置天气状态 WeatherSystem->SetVariableBool(FName("EnableRain"), true); WeatherSystem->SetVariableBool(FName("EnableSnow"), false); WeatherSystem->SetVariableBool(FName("EnableFog"), true);

    2.1 时间轴过渡优化

    直接切换会显得生硬。我们在Niagara中为每个Emitter添加`Float`变量`TransitionTime`(默认值1秒),在`Spawn Rate`模块中用`Lerp`实现渐变:`Lerp(0, NormalRate, Saturate(TotalTime / TransitionTime))`。同时,雨滴的透明度也做渐变,让天空从晴朗到暴雨有一个视觉过渡。

    2.2 性能监控与LOD

    在Niagara System的`Details`面板中,勾选`Enable LOD`,设置三个级别:

  • LOD0:完整效果(Spawn Rate 100%)
  • LOD1:粒子数降为60%(Spawn Rate 60%)
  • LOD2:禁用碰撞和飞溅(移除Collision模块)
  • 用`Distance to Camera`作为LOD切换依据,建议阈值:LOD0在20米内,LOD1在20-50米,LOD2在50米外。

    三、进阶技巧:与场景交互的天气效果

    3.1 雨水涟漪与地面反射

    在雨效Emitter中,当粒子碰撞到地面时,除了飞溅,还可以生成Decal。在`Spawn Burst Instantaneous`中,生成一个Decal Actor(通过`Spawn Actor`模块),类型选择`BP_RainRipple`,生命周期0.5秒,旋转随机。Decal材质使用`Material Domain = Deferred Decal`,Blend Mode = Translucent,用`Panner`节点让涟漪扩散。

    3.2 雪花融化和积雪厚度

    对于雪效,我们可以用`Render Target`记录积雪位置。在Emitter中,每帧将粒子的世界坐标写入一个`Canvas Render Target 2D`(分辨率256×256),然后在材质中采样这张RT,控制地面材质的`Snow Layer`参数(如Base Color变白,Roughness降低)。注意:RT的写入频率不要超过30fps,否则性能会崩溃。

    3.3 雾效与光源的交互

    雾效的`Volume Renderer`默认不响应动态光源。要实现丁达尔效应,需要在雾的材质中添加`Light Function`节点,采样场景的`Directional Light`位置,计算视线与光线夹角,产生光柱效果。具体做法:在雾的`Material`中,添加`Dot Product`计算`Camera Vector`与`Light Direction`,用`Power`节点控制光柱的锐利度(建议值8-12)。

    总结与进阶建议

    这套系统已经在我带的学员项目中跑过验证,在RTX 3060上,雨雪雾同时开启时帧率稳定在45fps以上(1080p)。如果追求极致性能,可以进一步优化:
    1. 使用GPU Compute Shader替代部分Niagara逻辑(需C++编写)
    2. 将雨滴的Sprite替换为Instance Static Mesh(如细长圆柱)
    3. 利用World Partition系统,只在玩家周围加载天气粒子

    下一期,我会讲如何用AIGC生成天气贴图——比如用Stable Diffusion生成不同强度的雨滴纹理,或雪花的晶形纹理,然后直接导入Niagara使用。如果你对AIGC+UE5感兴趣,记得关注我们的火星人教育公众号,回复“天气”获取本次案例的完整工程文件。

    常见问题 FAQ

    Q1:Niagara天气系统相比蓝图天气系统,性能优势有多大?
    A:Niagara的粒子计算在GPU上进行,而蓝图天气通常需要CPU处理每个粒子的逻辑。实测在相同粒子数(10万)下,Niagara的帧率比��图高40%-60%。但注意,Niagara的Volume Renderer对GPU显存要求较高,建议至少6GB显存。

    Q2:为什么我的雨滴看起来像“面条”而不是细丝?
    A:检查两个地方:一是`Sprite Renderer`的`Alignment`是否设为`Velocity Aligned`,确保粒子朝向运动方向;二是`Size`的Z轴是否足够大(建议1.5-2.0),同时缩小X和Y轴(0.1-0.2)。

    Q3:雪花堆积后,地面材质如何动态变白?
    A:使用`Material Parameter Collection`(MPC)存储积雪厚度值。在Niagara中,每帧用`Set Float Parameter`节点更新MPC的`SnowDepth`变量,然后在材质中采样这个值,控制Base Color和Roughness。注意:MPC的更新频率不要超过60fps。

    Q4:雾效在室内场景中太浓,如何根据环境自动调节?
    A:在Niagara的`User Exposed Parameters`中添加`FogDensityMultiplier`,并在关卡蓝图中用`Line Trace`检测玩家周围是否有阻挡物(如墙壁)。如果Trace检测到遮挡,将Multiplier设为0.3;否则设为1.0。

    Q5:天气切换时出现“闪烁”或“断层”怎么办?
    A:这是粒子生命周期过渡导致的。解决方法:在`Spawn Rate`模块中,使用`Lerp`函数做平滑过渡(过渡时间建议1-2秒),同时将粒子的`Alpha`也做渐变。如果问题依然存在,检查是否在Emitter的`Update`中使用了`Kill Particles`模块,这会强制销毁粒子,导致视觉断层。

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