GPU-Driven 水体渲染

一、效果展示

1.1 静帧效果

  • 目前渲染效果

  • 初版效果

1.2 动态效果


二、技术拆解

2.1 几何阶段

初版方案

  • 波形:分形噪声(FBM)

目前版本方案

  • 波形:Gerstner 波
  • 交互:波动方程
    • 波动方程原理实现 + 历史帧俯视角深度 Mask 对比区分动态静态物体 + 像素对齐 + 帧同步(保证固定时间长度迭代一次):

2.2 着色阶段

  • 基本表面着色:自写基础 PBR
  • 折射:分形噪声或 Gerstner 波扰动 OpaqueTexture
  • 间接光反射:SSPR(屏幕空间平面反射)→ 更新为双面 SSPR,以作为水下全反射效果:
  • 通透感表现:盗贼之海 SSS 方案 + 深度 Lerp → 盗贼之海 SSS 方案 + 可美术自定义化的深度颜色与可见度 Lerp(水下效果与水面着色兼顾):
  • 焦散 (Caustics)
    • 初版:基于屏幕空间的光子映射法
    • 目前版本
      • 反射焦散:基于屏幕空间的 Gather 方法
      • 折射焦散:根据 Shadowmap 的特性进行 CausticCascadeMap 级联写入 + Light-NDC 空间的 RayMarching + 世界空间印花:
  • 白沫 (Foam):基于 JFA (Jump Flooding Algorithm) 生成 SDF → 根据 Gerstner 波求得雅可比行列式(预备海面渲染)
  • 近水线侵蚀:屏幕空间检测近水范围 + 修改 OpaqueColor 色相饱和度明度:
  • 水线 (Waterline):根据摄像机近平面世界坐标与水面解析得到的世界坐标得到距离场 + Kawase 模糊 Capture_Opaque_Color + 明度降低:
  • 水下体积光:根据折射焦散 Pass 得到的 RT 做屏幕空间光线步进采样该 RT + WaterlineMaskRT

2.3 网格阶段

初版方案

  • 自适应曲面细分:自写基于距离的 Quad 曲面细分

目前版本方案

  • GPU-Driven 方案
    • Renderer Feature 驱动四叉树迭代法求原始面片 Size + Position
    • 基于曲面细分着色器的 fractional_even 四叉树细分逻辑
    • 视锥剔除(Frustum Culling):

三、引擎具体实现展示

3.1 GPU-Driven 架构

  • MaterialDataBuffer → 逐 Material Profile(OS)→ Buffer.setData
  • SubDivideQuadDataBuffer(Pos / Size / MaterialID)→ CPU 初步视锥体粗粒度剔除 → Buffer.setData → GPU-SubDivideRendererFeature 迭代(切分逻辑函数 + 更新 ArgumentBuffer)→ DrawInstanceIndirect(根据 InstanceID 拿到必备信息)→ 曲面细分着色器(切分逻辑函数)→ 保证不发生不平滑穿插现象:

3.2 类 CascadeMap 解决 Gerstner 波重复解析与波动方程模拟 RT 重复采样消耗

  • 问题背景:在吃水线效果、焦散(折射 + 反射)实现、水面顶点位移、视锥剔除逻辑、水面本体渲染中,都需要重复求解解析式,极大占用 ALU 算力。
  • 解决方案:以空间换时间的思路预计算 Gerstner 波的信息(法线、位移、挤压度):
  • 折射焦散基于 Nsight Graphics 的优化前后性能对比 (结论:就折射焦散而言,已有显著的性能提升)

3.3 Renderer Features 管线配置

  • 分别实现距离场、环境反射、焦散(旧版):
  • (目前版本):

3.4 Shader 参数面板

  • 旧版 Shader 面板
  • 新版 Shader 面板

四、部分效果个人思考过程与问题

4.1 旧版思考

  1. 水面顶点动画: 根据距离动态细分的 Quad 曲面细分着色器 —— 本人发现 Tri 曲面细分的距离动态细分过程不够平滑,故尝试使用 Quad 规则进行细分,并编写距离动态细分规则,较 Unity 内置的动态距离细分函数更平滑。
  2. 焦散: 在尝试了通过 Light 空间的光子映射法实现焦散后,本人发现焦散的生成可以直接调用 Unity 的级联 Shadowmap 来实现类级联焦散。后对于焦散的耗能问题,尝试实现 ScreenSpace 的焦散;不过 ScreenSpace 的焦散仅在正午有较好效果(屏幕外的信息缺失),但性能较好,特定场景下可针对性使用。
  3. 白沫: 深度差白沫有一定的局限性:视角变化会导致白沫区域变化产生违和感。故通过 JFA 算法建立实时 SDF 场。

4.2 目前版本思考

  1. 曲面细分 + 切分的平滑过渡: 在选择 fractional_evenfractional_odd 的细分模式过程中,前者的偶数细分特征十分适配切分四叉树的规则。这里可将 fractional_even 模式描述为:
    • 细分因子值为 :拓扑结构不变;
    • :生成新的点与线段但保持位置不变;
    • :平滑到对称位置。 故我们在设计切分细分函数时,只需要相邻边切分(细分)返回值的比值关系 ,并且避免极端情况(面片各边缘的切分逻辑返回结果过于迥异)导致的相邻边切分状态跳变。
  2. 视锥剔除的动态情况解决: FBM 与 Gerstner 波的核心区别:有无水平(XZ)的位移。故我们在做 Subdivide 与视锥剔除时(同时进行提升性能,省去了后续迭代的复杂度),需要应用 Gerstner 的位移信息以解决视锥边缘误剔除的问题。
  3. 目前版本架构问题与解决思路
    • 无法完美适配各种形状的水面 → 采用绘制 Lookup Watermap 的方法解决;
    • 遮挡剔除(Occlusion Culling)对于目前的架构是可以实现的。