UE5 材质 Water Shader

发布时间 2023-05-23 23:28:48作者: 爱莉希雅

目标

  • 水有许多特性,本篇将着重实现如下特性
    • 表面涟漪
    • 水深效果
    • 水的反射和折射
    • 海浪
    • 波光的焦散

表面涟漪

初步实现

image-20230520150152153
image

模拟水的运动

  • 现实中水的流动是较为混乱的,但上图的水流动过于统一。我们使用世界坐标的位置进行投影,且让水只在XY平面进行运动
    image-20230520152927951
  • 如何让水的流动混乱呢?很容易想到,我们需要让纹理图在不同方向上进行变换
    image-20230520154703210
    image

水深

影响水透明度的因素

  • 看向水面的角度
  • 水深。水越深,会变得越来越不透明

设置blend mode

  • 因为这里涉及到半透明渲染,我们需要选择"Blend Mode"为"Translucent"
    image-20230520155955692

测量水深

"SceneDepth" & "PixelDepth"

步骤

  • SceneDepth:以相机为视角,返回场景中离它最近的一个物体的距离且忽略透明物体
    image-20230520161006162
  • PixelDepth:以相机为视角,返回它到半透明物体的距离
    image-20230520161223225

实现

  • 初步实现
    image-20230520162419701
    image-20230520162441733

    从上图可以看出这正是我们想要的效果,从浅到深,刚开始有些黑色,然后变白

  • 纠正视角问题

    • 问题
      当我们垂直看去,得到的效果如下
      image-20230520162843968

      当我们水平看去,得到的效果如下
      image-20230520162939926

      可以看出因为角度的问题,求得的深度值也有所不同,角度越偏向水平,得到的深度值越大,透明度越低

    • 原因

      很明显水深并不是"SceneDepth - PixelDepth",而是垂直的深度,这里用相似即可实现
      image-20230520163533817

    • 实现
      image-20230520171132082
      image-20230520171310120
      image-20230520171334922

测量角度

目标

  • 当视角越来越垂直水面,水越来越透明
  • 当视角越来越平行水面,水越来越不透明

实现

水颜色的渐变

目标

  • 因为水的颜色收到深度的影响,因此我们需要创建两种颜色的水,一种代表深处的颜色,一种代表浅处的颜色

实现

image-20230520175855482
image-20230521145104621

以下是水深的全部实现内容
image-20230521145510602

水的反射和折射

UE中的反射

  • UE中的反射有五种不同的实现方法,且许多场景不单单使用一种反射而是多种反射方法相结合。在这里我们介绍其中三种

    • sky box

    • Light Probe(光照探针)

      简单来说,Light Probe给予场景中某个点向它周围收集光照的能力,并记录周围的光照(irradiance map)。随后对某个像素点进行渲染时,利用它附近的probe的光照信息估计该点所受光照
      img

      • 缺点
        • 只适用于静态物体和静态光照
    • 屏幕空间反射(Screen Space Reflections)

      简单来说,Screen Space Reflections以屏幕数据计算反射效果。因为它需要G-Buffer中的normal,所以只适用于延迟渲染

      • 缺点
        • 成本高
        • 只能反射屏幕内的物体,对屏幕外的无效

UE中的折射

  • UE5提供了两种折射模式,一种是基于物理的无normalmap的折射模式"Index of Refraction";另一种是不基于物理的normal的折射模式"Pixel Normal Offset"

  • 这两种模式各有长处

    • Index of Refraction模拟光线在介质间传播时的折射方式。适用于小物体,对于较大的物体很可能会带有瑕疵,因为当前屏幕中的物体的颜色很可能从屏幕外获取

    • Pixel Normal Offset以vertex normal为基础,计算每个pixel的normal和vertex normal的差异来得出折射偏移。适用于较大平面,因为无需从屏幕外读取数据。需要注意的是,若参数"Refraction Depth Bias" > 1,法线将沿平面平移

    • 参数Refraction Depth Bias

      Refraction Depth Bias用于防止距离较近的对象以尖锐的(acute)视角渲染到扭曲的表面。但可能会增加表面和折射位置的距离

启用反射和折射

  • 在根节点处启用"Screen Space Reflections"
    image-20230521153144962
  • 在根节点处选用"pixel normal offset"
    image-20230521153416422

海浪

海浪算法

思想

  • 水体渲染主要运用两个表面的模拟:一个用于表面网格的几何波动,另一个是网格上法线图的扰动。而水面高度由简单的周期波叠加表示
  • 因为水的波纹呈sin分布,所以我们基于简单的sin函数进行叠加可以得到一个连续函数,该函数描述水面上所有点的高度和方向

波的选择

  • 在正式开始前,我们需要了解不同类型的波浪应该用哪种波形
    • 对于受影响生成的波,应使用方向波。对于方向波,波的方向是在风的一定范围内画的
    • 对于平静的水面,生成的波并不是因为风(如瀑布),应使用圆形波。对于圆形波,波中心是在限定范围内任意画的
      image-20230521193006821

为什么选择Gerstner算法?

  • 因为Gerstner算法有一个特性,它将顶点朝着每个浪头顶部移动,从而形成更尖锐的波峰。而这一特性,正是我们所需要的
    img

从sin函数开始

  • 由于sin函数在x轴方向不变,在y轴方向上下移动,使得它形成的面更为圆滑,因此sin函数更适合平静的湖面
参数选择
  • 对于波的形成,我们需要考虑以下几个参数

    1. 波长(\(L\)):world space中波峰到波峰的间距。\(L\)\(\omega\)(角频率)的关系 \(\omega = \frac{2\pi}{L}\)
    2. 振幅(A):水平面到波峰的间距
    3. 速度(S):波峰每秒移动的距离。为方便将S表示为相位常数\(\varphi = S \frac{2\pi}{L}\)
    4. 方向(D):垂直于波面且的水平向量
  • 波的状态定义为位置(x,y)和时间t的函数:\(W_i(x,y,t) = A_i \times sin(D_i · (x,y) \times \omega_i + t \times \varphi_i)\)

    img

  • 所有波i的总表面:\(W_i(x,y,t) = \sum A_i \times sin(D_i · (x,y) \times \omega_i + t \times \varphi_i)\)

法线和切线
  • 切线和副切线分别是y方向和x方向上的偏导
  • 现有2d平面的任意点\((x,y)\),表面的3d坐标\(p(x,y,t) = (x,y,H(x,y,t))\)
    • 切线即为对y轴求偏导:\(T(x,y) = (0, 1, \frac{\partial}{\partial y}(H(x,y,t))\)
    • 副切线即为对x轴求偏导:\(B(x,y) = (1, 0, \frac{\partial}{\partial x}(H(x,y,t)))\)
    • 法线即为副切线和切线的叉乘:\(N(x,y) = (-\frac{\partial}{\partial x}(H(x,y,t)), -\frac{\partial}{\partial y}(H(x,y,t), 1)\)

Gerstner波

  • Gerstner波有一个特性,它将顶点朝着每个浪头顶部移动,从而形成更尖锐的波峰,这适用于粗犷的海洋
  • Gerstner波中每一个水分子都在做圆周运动,这意味着x,y都在变化(而sin函数只有y变化,x不变),且水分子在波峰聚集,在波谷分散,越靠近水面,圆周运动的半径越大
定义
  • Gerstner波定义

    \(P(x,y,t) = \left(\begin{array}{cc} x + \sum(Q_i A_i \times D_i.x \times cos(\omega_i D_i·(x,y) + \varphi_i t)) \\ y + \sum(Q_iA_i \times D_i.y \times cos(\omega_i D_i·(x,y) + \varphi_i t)) \\ \sum(A_i sin(\omega_i D_i·(x,y) + \varphi_i t)) \end{array}\right)\),其中\(Q_i = \frac{1}{\omega_i A_i}\)

  • 对于单个波i,\(Q_i = 0\)时,Gerstner波为sin波

    • 使用\(Q_i = \frac{Q}{\omega_i A_i \times numWaves}\)可以控制波的平滑或尖锐程度
    • 需要注意的是,应当避免使用过大的\(Q_i\),这会导致波峰形成环
      img
切线和法线
  • \(WA = \omega_i \times A_i \\ S() = \sin(\omega_i \times D_i · P + \varphi_i t) \\ C() = cos(\omega_i \times D_i · P + \varphi_i t)\)

  • 副切线\(B = \left(\begin{array}{cc} 1-\sum(Q_i \times D_i.x^2 \times WA \times S()) \\ -\sum(Q_i \times D_i.x \times D_i.y \times WA \times S()) \\ \sum(D_i.x \times WA \times C()) \end {array}\right)\)

  • 切线\(T = \left(\begin{array}{cc} -\sum(Q_i \times D_i.x \times D_i.y \times WA \times S()) \\ 1 - \sum(Q_i \times D_i.y^2 \times WA \times S()) \\ \sum(D_i.y \times WA \times C()) \end{array}\right)\)

  • 法线\(N = \left(\begin {array}{cc} -\sum(D_i.x \times WA \times C()) \\ -\sum(D_i.y \times WA \times C()) \\ 1 - \sum(Q_i \times WA \times S()) \end{array}\right)\)

参数
  • 波长

    这里波长的选择不是根据现实而定,而是使用少数几个波达到最大效果。因此我们选择中等的波长,以它的\(\frac{1}{2}\)\(2\)倍间产生任意波长

  • 波速

    波速与波长L、重力g(国际单位\(9.8m/s^2\))相关:\(S = \sqrt{g \times \frac{2\pi}{L}}\)

  • 振幅

    在Shader中指定一个系数,由美术人员对波长指定对应的合适振幅

  • 方向

    波的运动方向和其他参数完全独立,可以自由选择

实现

  • 解决水面交界的绿线

    从上图可以看到水面和岩石及墙体的交界都有一道绿线,这是由"depth fade"节点造成的。"depth fade"在我们的实现中仅仅用于水的不透明度,但我们却将其用在水的颜色
    image-20230521162239829

  • 降低水和物体交界处的折射效果以交界处的硬线
    image-20230521162527060
    image-20230521174405103
    image-20230521174551698

海浪

  • 将上述公式套一下即可
    image-20230523110918629
    image

波光焦散

目标

  • 在现实中,当太阳照射水面,水面会扭曲这些光纤从而生成特别酷炫的纹理
    image-20230523161227367

原理

  • 对于这种纹理也是可以实现的,实现方式是贴花(decal)

    简单来说,运用在实时渲染中的贴花技术是屏幕空间的延迟贴花——利用现有的G-Buffer,直接将贴花投射在物体表面

  • 运作时间

    写入G-Buffer后,屏幕空间的后处理前

  • 与TBN类似,需要将世界空间转换到贴花空间,这样才能得到正确的贴花位置及其纹理。随后计算投影位置即可

根节点设置

  • 这里我们需要新建一个材质,并将根节点按如下设置
    image-20230523173017968

纹理

image-20230523173218299image-20230523173233120
image-20230523174652224

实现

初步实现

image-20230523173442096image-20230523173543387

加入动画

  • 简单的动画效果
    image-20230523174250523
    image

  • 扭曲效果

    目前的动画太有规律了,但现实中更像没有规律的。因此,我们需要将平移效果带点起伏
    image-20230523175928464
    image

  • 继续优化

    可以看到确实有那味儿了!若为了更好的效果,可以再加一个不同的法线扰动效果
    image-20230523181035090
    image

修正投影

  • 目前,我们的贴花是基于z轴投影的,这会导致z轴的贴花不会有理想效果
    image-20230523181753982
  • 如何修正呢?很简单,三个轴都进行投影
    image-20230523183811632
    image

reference

基于 Probe 的实时全局光照方案(Probe-based Global Illumination) - KillerAery - 博客园 (cnblogs.com)

[Siggraph15]Stochastic Screen-Space Reflections - 知乎 (zhihu.com)

虚幻引擎中的屏幕空间反射 | 虚幻引擎5.0文档 (unrealengine.com)

使用像素法线偏移实现折射 | 虚幻引擎文档 (unrealengine.com)

使用像素法线偏移实现折射 | 虚幻引擎文档 (unrealengine.com)

在虚幻引擎中使用像素法线偏移实现折射 | 虚幻引擎5.1文档 (unrealengine.com)

使用折射 | 虚幻引擎文档 (unrealengine.com)

GPU Gems1

[Game-Programmer-Study-Notes/README.md at master · QianMo/Game-Programmer-Study-Notes · GitHub](https://github.com/QianMo/Game-Programmer-Study-Notes/blob/master/Content/《GPU Gems 1》全书提炼总结/README.md#一、-用物理模型进行高效的水模拟(effective-water-simulation-from-physical-models))

水体渲染之Gerstner波形理解与推导 - 知乎 (zhihu.com)

贴花 | 虚幻引擎文档 (unrealengine.com)

谈谈游戏影视中的贴花decals技术 - 知乎 (zhihu.com)