Fluid Flux2.0海浪原理拆解
【USparkle专栏】如果你深怀绝技,爱“搞点研究”,乐于分享也博采众长,我们期待你的加入,让智慧的火花碰撞交织,让知识的传递生生不息!
大概一年前,在油管上看到这个视频:
Fluid Flux 2.0 - Coastline [Unreal Engine 5]
除了效果很好,更重要的是看到简介中写着:“并非实时模拟”、“预烘焙”:
这意味着不会有太大性能开销,于是顿觉价值大大的。
但当时事情多,就只收藏了一下,没细研究。后来还看到有人移植《戴子玲:Fluid Flux 2.0 的近岸海浪》。
虽然我们本来就是UE项目,移植更方便,但是想仔细看下原理。
通过删除次要内容,拆出来核心逻辑如下:
其中WorldCoastlineMap用于生成海岸线,WavePofileMap用于单个海浪动画和置换,WaveProfileDistance控制海浪离岸的距离,WaveProfileTime为动画时间。
效果:(将区域范围缩小到了100,手动拖动WaveProfileTime)
视频:https://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_FluidFlux2/4.mp4
可以看到海浪的翻滚效果。
下面来分析原理,主要是搞懂:
- WorldCoastlineMap和WavePofileMap这两个图的含义。
- 由WorldCoastlineMap生成海岸线的原理。
- 由WavePofileMap生成单个动画的原理。
一、WorldCoastlineMap含义
WorldCoastlineMap:
R通道明显是SDF,表示“到水的距离”,在材质中,R先被0.5减,然后连到Distance,所以Distance含义为“到岸的距离”。
再看G、B通道,在材质中,它们也是先被0.5减,然后连到Direction:
即Direction=(0.5,0.5)-(G,B),我们看它表示哪个方向,如下图:
(因为G通道看起来像从左往右打光,所以我们用左亮右暗的球代表它,因为B通道看起来像从上往下打光,所以我们用上亮下暗的球代表它)
即当我们把岛看成球时,得到的Direction呈放射状(图中是(G,B)-(0.5,0.5),箭头向外,如果是Direction=(0.5,0.5)-(G,B),则箭头向里),所以Direction表示的是“指向海岸的方向”。
至此Distance和Direction的含义都清楚了,一个是(从陆地)“到海岸的距离”,一个是(从陆地)“指向海岸的方向”。
二、海岸线UV计算
由Distance和Direction计算海岸线UV,虽然连线摆在那儿,但默认的WorldCoastlineMap较复杂,难看清所以然。于是有必要自己弄一个简单的WorldCoastlineMap,就一个如图所示左高右低的简单斜面,Distance值左大右小,Direction=(-1,0,0):
则只需将材质中的Distance和Direction输入改一下:
结果:
可见,和预期是一致的。这样就有了一个更简单的例子可以观察。
为了方便做各种试验,在Houdini中复现了一下(为了代码简洁,参数随手写死的,与UE中的并不一致):
注意几点:
- 用Colormap采样出来的贴图(我用的TGA格式)颜色要做Gamma矫正Pow 0.45。
- UE中WaveProfileMap的Wrap模式U是设置为Wrap,V设置为Clamp:
但Houdini中Colormap的Wrap模式不能U和B分开控制:
所以只能把UV都设置成Clamp(Houdini中叫Streak),然后通过对U加Frac,人为实现U向的Tiling。也就是如下图所示,将代码从左边改成右边:
这样改是不影响最终结果的,而且由于U不再超出0到1,所以更便于可视化。
U这样改写以后,V就可以直接写成:
v=u-dis+offset;
其中offset就相当于UE里的WaveProfileDistance,用于控制浪到海岸的距离。
这样一来,公式比UE里连的更简洁了,下面就基于此来理解原理。
V可视化长这样:
以下为其演变过程:
可见:
- 加P.y是为让条纹倾斜。
- 加noise有两个效应:
- 效应1:条纹产生扭曲。
- 效应2:条纹V方向上渐变产生交错。
最右一行是将WaveProfileMap换成了如下自定义贴图(为了观察UV):
当time变化时,效果如下:
视频:https://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_FluidFlux2/18.mp4
如果不加noise,则如下:
视频:https://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_FluidFlux2/19.mp4
至此海岸线UV计算原理已看清楚。
三、海浪翻滚动画实现
从上面自定义贴图的视频中可以看到,如果不加noise的话,看起来就是贴图在沿着条纹平移。把自定义贴图换回WaveProfileMap,并加上Vector Disp,加noise与不加noise效果对比如下:
视频:https://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_FluidFlux2/20.mp4
可见,浪前沿的不规则形状是靠noise的效应1“条纹产生扭曲”实现的,浪前沿顿锐交错是靠noise的效应2“条纹V方向上渐变产生交错”实现的。在未加noise之前,浪只感觉是在沿着条纹走,丝毫感觉不到垂直于条纹向前推的感觉,但加上noise之后,浪沿条纹走的感觉就削弱了,开始有了垂直于条纹推进的错觉。
在浪垂直于条纹向前推进的过程中,还会有翻滚效果,拉近可以看得比较清楚:
视频:https://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_FluidFlux2/21.mp4
所以最后一个问题就是:浪的翻滚是靠什么实现的。
这涉及到WaveProfileMap的含义。
打开UE工程的WaveProfileMap关卡,可以看到制作WaveProfileMap的方式,是用一排样条线做出了一个卷曲的海浪形状:
这可以理解为一个完整的3D海浪,也可以理解为一套2D序列(每个切片为一帧2D剪影)。
后一种理解更符合此方法的本意。
在Houdini用一个移动立面进行截取,可以直观看到2D海浪动画的样子:
视频:https://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_FluidFlux2/23.mp4
所以它就是在条纹上每个点处播放这个2D动画:
由于条纹V方向上有均匀渐变(不加noise的情况下),所以V方向上各处2D动画存在均匀的相位差,整体看上去就像一个完整3D海浪在沿着条纹平移一样。然后通过加noise使条纹扭曲,并在V方向上产生相位产生交错,从而打破平移感。
四、不规则海岸
上面通过构造最简的直线形海岸,大致看清了原理。依此原理,容易生成其它形状海岸,比如(为了方便,仍然在Houdini里验证):
(1)圆形:
视频:https://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_FluidFlux2/26.mp4
(2)自定义形状:
视频:https://uwa-ducument-img.oss-cn-beijing.aliyuncs.com/Blog/USparkle_FluidFlux2/28.mp4
五、不足(个人观点)
我觉得海岸线UV计算的方法很巧妙,但也有不足,最主要的不足是公式不对称,从而不同方向得到的UV品质不一致,导致某些方向上海浪的形不够好,比如:拉伸过于严重、移动速度沿条纹方向过快等。
是否可以考虑不用通过DIS和DIR简单的数学运算生成UV,而是用离线方法生成更好的UV,从而得到更好的效果,或许是一个改进方向。
这是侑虎科技第1689篇文章,感谢作者杨超wantnon供稿。欢迎转发分享,未经作者授权请勿转载。如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。(QQ群:793972859)
作者主页:https://www.zhihu.com/people/wantnon
再次感谢杨超wantnon的分享,如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。(QQ群:793972859)