水下气泡与焦散光效:UE5 环境特效的高级技巧
上周有位学员在群里发了一段水下场景的测试视频,气泡像塑料球一样僵硬上浮,焦散光斑完全没动——他问我:“为什么我的水下世界总像一缸死水?”这个问题很典型。很多人在做水下环境时,要么用 Niagara 随便撒点泡泡,要么把 Light Function 里的焦散贴图一贴了事,结果做出来的东西缺乏“水的呼吸感”。
今天我们就从两个核心维度拆解:气泡的物理级动态 和 焦散光效的实时映射。全程基于 Unreal Engine 5.3.2 版本,Niagara 版本为 5.3,涉及的光照系统使用 Lumen 实时全局光照。准备好,我们直接开干。
一、气泡特效:从“塑料球”到“有生命的气泡”
1.1 问题诊断:为什么你的气泡不像真的?
先看学员的 Niagara 发射器设置:粒子生命 3 秒,速度向上 50cm/s,大小随机 0.5-1.5cm。这个参数组合下,所有气泡都沿着直线匀速上升,碰到场景物体直接穿透——这就是塑料感的来源。
真实气泡有三个关键特征:
- 上升路径非直线:受水体微扰动影响,气泡会呈螺旋或摇摆��上升
1.2 操作步骤:用 Niagara 打造真实气泡系统
Step 1:创建基础发射器
在 Content Browser 右键 → FX → Niagara System → 选择 Simple Sprite Burst。删除默认的 `Spawn Burst Instantaneous` 模块,改为 `Spawn Rate`,设置 `Spawn Rate` = 50(每秒发射 50 个气泡)。
Step 2:启用物理碰撞
在 Particle Update 阶段添加 `Collision` 模块。关键参数:
重点:需要确保场景中的静态网格体有 `Collision Presets` 设置为 `BlockAll`,否则气泡会穿透。建议在项目设置 → Physics → 勾选 `Enable World Bounds`,防止气泡飞出世界边界。
Step 3:实现螺旋上升路径
默认 `Add Velocity` 模块只能给恒定速度。我们需要在 Particle Update 中自定义一个 `Attribute Reader` + `Scale Vector` 组合:
1. 添加 `Add Velocity` 模块,设置 `Velocity` = (0, 0, 80)(向上 80cm/s)
2. 添加 `Noise` 模块,类型选择 `Perlin Noise`,设置:
– `Noise Amplitude`:15(水平偏移幅度)
– `Noise Frequency`:0.5(波动频率)
– `Noise Offset`:绑定 `Particle.ID`(每个粒子不同噪声相位)
3. 将 `Noise` 输出连接到 `Add Velocity` 的 `Velocity` 输入,并用 `Scale Vector` 将噪声值映射到 X 和 Y 轴(Z 轴保持 80 向上)
这样每个气泡都会以不同的螺旋半径和频率上升。实测效果:气泡从水面看像在跳华尔兹。
Step 4:添加表面反射
气泡材质使用 `Translucent` 混合模式,`Lighting Mode` 选 `Surface Translucency Volume`。核心节点:
Step 5:优化性能
在 Niagara 发射器属性中:
二、焦散光效:让水面成为“动态透镜”
2.1 焦散的原理与常见误区
焦散(Caustics)本质是光线通过水面折射后,在底部或物体表面形成的亮斑集中区域。很多教程教的方法是把一张静态焦散贴图贴到 Light Function 上,然后通过时间偏移让图案滑动——这只能做出“贴图在动”的效果,而不是“光斑在移动”。
真正的焦散应该具备:
2.2 操作步骤:基于 Wave 函数的实时焦散
Step 1:创建焦散材质函数
在 Content Browser 新建 `Material Function`,命名为 `MF_Caustics_RealTime`。节点架构如下:
1. 输入参数:
– `UV`:TextureCoordinate(默认 0-1)
– `Time`:ScalarParameter(绑定游戏时间)
– `WaveHeight`:从场景水面材质获取(通过 `Material Parameter Collection`)
2. 核心算法(用 Custom HLSL 节点):
float2 uv = UV * 3.0; // 缩放焦散密度
float wave1 = sin(uv.x 10.0 + Time 2.0) cos(uv.y 8.0 + Time * 1.5);
float wave2 = sin(uv.y 12.0 + Time 1.8) cos(uv.x 9.0 + Time * 2.2);
float caustic = saturate(wave1 wave2 2.0 + 0.5);
return caustic * 0.8 + 0.2; // 控制对比度
3. 将结果通过 `Multiply` 节点乘以 `WaveHeight`(波浪高度越高,焦散强度越大)
Step 2:应用到 Water 材质
打开你的水面材质(使用 `Water` 材质域),在 `Base Color` 或 `Opacity` 通道中插入 `MF_Caustics_RealTime`。关键设置:
Step 3:创建焦散 Light Function
1. 新建材质,`Material Domain` 选 `Light Function`,`Blend Mode` 选 `Additive`
2. 将 `MF_Caustics_RealTime` 的输出连接到 `Emissive Color`,并乘以 `Light Color`(从 `Light Function` 节点获取)
3. 在场景中放置一个 `Directional Light`,在 Details 面板的 `Light Function` 处指定该材质
4. 调整 `Light Function Scale` 为 500(对应场景尺寸),`Light Function Fade Distance` 设为 10000
此时移动光源,焦散图案会实时改变形态——因为光源角度变化导致 `World Position` 偏移,从而影响 UV 采样。
Step 4:投射到动态物体
默认 Light Function 只影响静态光照。要让焦散投射到角色或 NPC 上,需要:
1. 在项目设置 → Rendering → 勾选 `Support Global Shading Model on Dynamic Objects`
2. 在目标材质的 `Customized UV` 中,添加 `World Aligned Texture` 节点,采样焦散贴图
3. 将 `Object Position` 转换为 UV 坐标(用 `Transform` 节点从 World 转 Local)
这样角色身上的焦散会随角色移动而自然变化,而不是像贴纸一样焊在皮肤上。
三、整合与优化:让两种特效联动
3.1 气泡影响焦散
在 Niagara 发射器中添加 `Particle Parameter` 模块,输出 `Particle Position` 到 `Material Parameter Collection`。然后在水面材质的焦散函数中,根据气泡位置产生局部扰动:
这样气泡上浮时,下方的焦散光斑会产生“抖动”效果,仿佛气泡在搅动光线。
3.2 性能预算控制
| 特效组件 | 预估性能消耗(RTX 3070) | 优化策略 |
|———|————————|———|
| 气泡粒子(500个) | 0.3ms | 使用 GPU Sprites,关闭阴影 |
| 焦散 Light Function | 0.15ms | 降低分辨率到 512×512 |
| 水面波浪计算 | 0.2ms | 使用 Tessellation 减半 |
| 总开销 | 0.65ms | 控制在 1ms 内(60fps 目标) |
3.3 ��见问题排查
四、总结与进阶建议
我们今天用 Niagara 和材质函数实现了两个核心技能:
1. 气泡的物理碰撞 + 噪声螺旋路径 + 薄层干涉材质
2. 基于波浪函数的实时焦散 Light Function + 动态物体投射
这些技巧不仅适用于水下场景,还能拓展到:
进阶建议:
如果你想让焦散效果更炸裂,可以尝试用 `Render Target` 实时渲染水面高度图,然后作为焦散材质的输入——这样焦散会完美匹配水面每一帧的波浪形态,但性能消耗较大,适合电影级镜头。
最后,强烈建议你在场景中加一个缓慢旋转的 `Rect Light`,观察焦散在物体表面的动态变化。真正的沉浸感,往往来自这些细微的联动。
—
常见问题 FAQ
Q1:气泡碰撞后直接消失了,怎么让它分裂成小气泡?
A:在 Niagara 的 `Collision` 模块中,设置 `On Collision` 事件 → `Spawn Particles` → 数量为 2-3,并设置子粒子大小为原粒子的 30%-50%。注意子粒子的 `Spawn Rate` 要设为 0(不持续生成),否则会无限分裂。
Q2:焦散 Light Function 在 VR 模式下失效怎么办?
A:VR 渲染路径默认禁用了 Light Function。在 `Engine.ini` 中添加:`r.LightFunctionQuality=1` 和 `r.Mobile.LightFunction=1`。同时确保你的 VR 项目使用的是 Forward Shading 而非 Deferred。
Q3:水面波浪和焦散不同步,看起来像两个独立系统?
A:这是最常见的问题。原因在于波浪计算和焦散计算使用了不同的时间变量。解决方案:用 `Material Parameter Collection` 统一存储 `Time` 和 `Wave Height`,两个材质都从同一个 MPC 读取。另外,波浪的 `World Position Offset` 必须与焦散的 UV 偏移使用相同的波形函数参数。
Q4:气泡在角色身上产生奇怪的阴影?
A:气泡材质如果使用 `Translucent` 混合模式,默认会投射半透明阴影。在材质细节面板关闭 `Cast Shadow` 即可。如果仍需阴影效果,使用 `Masked` 混合模式,并设置 `Opacity Mask Clip Value` 为 0.5。
Q5:焦散投射到透明物体(如玻璃)上时出现重叠混乱?
A:透明物体默认不接收 Light Function。需要在该透明材质的 `Shading Model` 中启用 `Surface Forward Shading`,并在 `Forward Shading` 组中勾选 `Receive Decals`(Light Function 本质是一种 Decal)。同时,透明物体的 `Sort Priority` 要低于水面。

评论(0)