目标
- 水有许多特性,本篇将着重实现如下特性
- 表面涟漪
- 水深效果
- 水的反射和折射
- 海浪
- 波光的焦散
表面涟漪
初步实现
模拟水的运动
- 现实中水的流动是较为混乱的,但上图的水流动过于统一。我们使用世界坐标的位置进行投影,且让水只在XY平面进行运动
- 如何让水的流动混乱呢?很容易想到,我们需要让纹理图在不同方向上进行变换
水深
影响水透明度的因素
- 看向水面的角度
- 水深。水越深,会变得越来越不透明
设置blend mode
- 因为这里涉及到半透明渲染,我们需要选择"Blend Mode"为"Translucent"
测量水深
"SceneDepth" & "PixelDepth"
步骤
- SceneDepth:以相机为视角,返回场景中离它最近的一个物体的距离且忽略透明物体
- PixelDepth:以相机为视角,返回它到半透明物体的距离
实现
-
初步实现
从上图可以看出这正是我们想要的效果,从浅到深,刚开始有些黑色,然后变白
-
纠正视角问题
-
问题
当我们垂直看去,得到的效果如下
当我们水平看去,得到的效果如下
可以看出因为角度的问题,求得的深度值也有所不同,角度越偏向水平,得到的深度值越大,透明度越低
-
原因
很明显水深并不是"SceneDepth - PixelDepth",而是垂直的深度,这里用相似即可实现
-
实现
-
测量角度
目标
- 当视角越来越垂直水面,水越来越透明
- 当视角越来越平行水面,水越来越不透明
实现
-
初步实现
-
添加Normal,更改光照
将光照改为"Surface TranslucencyVolume"
-
减弱水和物体交界的硬线
DepthFade的原理不懂的可以看这UE5 材质 基础知识 - 爱莉希雅 - 博客园 (cnblogs.com)
水颜色的渐变
目标
- 因为水的颜色收到深度的影响,因此我们需要创建两种颜色的水,一种代表深处的颜色,一种代表浅处的颜色
实现
以下是水深的全部实现内容
水的反射和折射
UE中的反射
-
UE中的反射有五种不同的实现方法,且许多场景不单单使用一种反射而是多种反射方法相结合。在这里我们介绍其中三种
-
sky box
-
Light Probe(光照探针)
简单来说,Light Probe给予场景中某个点向它周围收集光照的能力,并记录周围的光照(irradiance map)。随后对某个像素点进行渲染时,利用它附近的probe的光照信息估计该点所受光照
- 缺点
- 只适用于静态物体和静态光照
- 缺点
-
屏幕空间反射(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"
- 在根节点处选用"pixel normal offset"
海浪
海浪算法
思想
- 水体渲染主要运用两个表面的模拟:一个用于表面网格的几何波动,另一个是网格上法线图的扰动。而水面高度由简单的周期波叠加表示
- 因为水的波纹呈sin分布,所以我们基于简单的sin函数进行叠加可以得到一个连续函数,该函数描述水面上所有点的高度和方向
波的选择
- 在正式开始前,我们需要了解不同类型的波浪应该用哪种波形
- 对于受风影响生成的波,应使用方向波。对于方向波,波的方向是在风的一定范围内画的
- 对于平静的水面,生成的波并不是因为风(如瀑布),应使用圆形波。对于圆形波,波中心是在限定范围内任意画的
为什么选择Gerstner算法?
- 因为Gerstner算法有一个特性,它将顶点朝着每个浪头顶部移动,从而形成更尖锐的波峰。而这一特性,正是我们所需要的
从sin函数开始
- 由于sin函数在x轴方向不变,在y轴方向上下移动,使得它形成的面更为圆滑,因此sin函数更适合平静的湖面
参数选择
-
对于波的形成,我们需要考虑以下几个参数
- 波长(\(L\)):world space中波峰到波峰的间距。\(L\)和\(\omega\)(角频率)的关系 \(\omega = \frac{2\pi}{L}\)
- 振幅(A):水平面到波峰的间距
- 速度(S):波峰每秒移动的距离。为方便将S表示为相位常数\(\varphi = S \frac{2\pi}{L}\)
- 方向(D):垂直于波面且的水平向量
-
波的状态定义为位置(x,y)和时间t的函数:\(W_i(x,y,t) = A_i \times sin(D_i · (x,y) \times \omega_i + t \times \varphi_i)\)
-
所有波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\),这会导致波峰形成环
切线和法线
-
设 \(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"在我们的实现中仅仅用于水的不透明度,但我们却将其用在水的颜色
-
降低水和物体交界处的折射效果以交界处的硬线
海浪
- 将上述公式套一下即可
波光焦散
目标
- 在现实中,当太阳照射水面,水面会扭曲这些光纤从而生成特别酷炫的纹理
原理
-
对于这种纹理也是可以实现的,实现方式是贴花(decal)
简单来说,运用在实时渲染中的贴花技术是屏幕空间的延迟贴花——利用现有的G-Buffer,直接将贴花投射在物体表面
-
运作时间
写入G-Buffer后,屏幕空间的后处理前
-
与TBN类似,需要将世界空间转换到贴花空间,这样才能得到正确的贴花位置及其纹理。随后计算投影位置即可
根节点设置
- 这里我们需要新建一个材质,并将根节点按如下设置
纹理
实现
初步实现
加入动画
-
简单的动画效果
-
扭曲效果
目前的动画太有规律了,但现实中更像没有规律的。因此,我们需要将平移效果带点起伏
-
继续优化
可以看到确实有那味儿了!若为了更好的效果,可以再加一个不同的法线扰动效果
修正投影
- 目前,我们的贴花是基于z轴投影的,这会导致z轴的贴花不会有理想效果
- 如何修正呢?很简单,三个轴都进行投影
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)
标签:omega,sum,Shader,折射,times,Water,UE5,com,sin From: https://www.cnblogs.com/chenglixue/p/17426704.html