水下气泡与焦散光效: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 Mode`:`Physics`(使用物理引擎)
  • `Restitution`:0.3(弹性系数,气泡碰撞后不会完全弹起)
  • `Friction`:0.1(降低摩擦力,模拟流体环境)
  • `Collision Radius`:绑定粒子大小(在 Details 面板勾选 `Use Particle Radius`)
  • 重点:需要确保场景中的静态网格体有 `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`。核心节点:

  • `Fresnel` 节点:`Exponent` 设为 3.0,控制边缘反射强度
  • `Reflection Vector`:连到 `Scene Color` 节点做环境反射
  • `Thin Film` 节点:`Thickness` 设为 0.5,产生彩虹色干涉(真实气泡特有的效果)
  • 气泡材质节点

    Step 5:优化性能

    在 Niagara 发射器属性中:

  • `Max Particles`:500(水面场景够用)
  • `Update Group`:`Async`(异步更新,避免卡主线程)
  • `Cull Mode`:`Distance Culling`(设置 3000cm 以外不渲染)
  • 二、焦散光效:让水面成为“动态透镜”

    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`。关键设置:

  • `Shading Model`:`Thin Translucent`(薄半透明)
  • `Water Parameters`:`Wave Amplitude` 设为 50,`Wave Length` 设为 200
  • 将材质的 `World Position Offset` 连接到 `Wave` 函数输出(让水面顶点随波浪位移)
  • 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`。然后在水面材质的焦散函数中,根据气泡位置产生局部扰动:

  • 用 `Distance` 节点计算像素到最近气泡的距离
  • 当距离小于 50cm 时,对 UV 做随机偏移(模拟气泡折射光线)
  • 这样气泡上浮时,下方的焦散光斑会产生“抖动”效果,仿佛气泡在搅动光线。

    3.2 性能预算控制

    | 特效组件 | 预估性能消耗(RTX 3070) | 优化策略 |
    |———|————————|———|
    | 气泡粒子(500个) | 0.3ms | 使用 GPU Sprites,关闭阴影 |
    | 焦散 Light Function | 0.15ms | 降低分辨率到 512×512 |
    | 水面波浪计算 | 0.2ms | 使用 Tessellation 减半 |
    | 总开销 | 0.65ms | 控制在 1ms 内(60fps 目标) |

    3.3 ��见问题排查

  • 气泡穿透水面:检查 `Particle Collision` 的 `Collision Channel` 是否包含 `Water` 通道(默认不包含,需在 Project Settings 自定义)
  • 焦散闪烁:`Light Function` 的 `Shadow Resolution` 调高到 2048,同时开启 `Temporal Anti-Aliasing`
  • 气泡在远处消失:`Cull Mode` 的 `Distance Culling` 阈值设置太小,改为 `View Dependent` 模式
  • 四、总结与进阶建议

    我们今天用 Niagara 和材质函数实现了两个核心技能:
    1. 气泡的物理碰撞 + 噪声螺旋路径 + 薄层干涉材质
    2. 基于波浪函数的实时焦散 Light Function + 动态物体投射

    这些技巧不仅适用于水下场景,还能拓展到:

  • 熔岩表面的气泡(改颜色和透明度)
  • 玻璃器皿中的折射光斑(焦散 + 透明材质)
  • 科幻护盾的能量波动(修改噪声频率和颜色)
  • 进阶建议

  • 学习 `Wave Function` 的数学原理(Gerster 波叠加),可以做出更真实的海面
  • 研究 `Substrate` 材质系统(UE5.3+),实现半透明物体的多层散射
  • 尝试 `Niagara Data Interface` 读取流体模拟数据,驱动气泡运动
  • 如果你想让焦散效果更炸裂,可以尝试用 `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` 要低于水面。

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