每天一个shader技巧day3 - Gerstner Wave
day2的时候说到了模拟水纹水波,想到了另一个常用的波函数-Gerstner Wave,今天就来说说这个为什么需要这个波函数以及它的应用。
先看看海浪图

不难看出,现实世界的海浪是有尖锐的峰顶的,比较波涛汹涌。那不管是sin或者cos函数,都没有一个尖锐的峰顶去模拟这个尖锐陡峭的峰顶。
那么为什么sin或cos函数都是圆润的呢,我们把这两个函数离散化一下,变成分散的点。
Sin wave

可以看到对于sin或cos函数,对于不同横坐标的点,每个点只会往上下两个方向移动,对于sin波可以有如下表达
A = 振幅, l = 控制周期, v = 移动速度, t = 时间。 是为了把周期从0到2
缩放到0到
shadertoy简单代码如下

Gerstner Waves
因为sin或cos这些波函数无法正确的模拟我们想要的波浪效果,所以我们需要一个可以更加准确描述风所造成的海浪的波的表达,那就是Grestner波了(查找资料过程中了解到了一个Stokes波,也是对水波浪的一个建模,但还挺复杂,有空再看)。先看看Grestner波做了什么事

现在,对于每个离散的点,除了上下移动,还会左右移动,并且是一个圆周运动。而且在波峰位置,点会更加聚集,在波谷位置,点就相对分散。那么既然是圆周运动自然可以想到圆的参数方程

那么我们对每个离散的点的x,y做一个这个变化就行了,看看代码

可以看到图片有被拉伸的效果,其实做到这里就差不多。但是如果你拉高(振幅)或者缩小
(周期的话)会看到一个打结的现象

那么这肯定不是我们想要的,如果在模拟水波的时候出现这种情况就会有一个断裂的现象

既然是振幅和周期引起的,那么我们就对其建立一个制约关系,去解决这个问题。这里直接说结论 (振幅 * 周期 <= 1)时才不会有这个断裂的问题。根据这个不等式,我们可以引入一个新的变量来控制振幅的值,引入新变量后公式为
代码如下
最后需要考虑的便是速度这一块了,因为这是对水波浪的一个模拟,那么速度肯定不是无限快的,波的速度并不是任意的,其速度与波长和重力相关,直接上公式,代码如下

二维模拟的情况到这就差不多了,推广到三维的话也是差不多,只需要在z轴做一个和x轴相同的操作即可(可以使用一个权重控制x轴和z轴的偏移贡献)。同时同一个点的Gerstner波是可以叠加的,具体做法可以将生成波的这个过程封装成一个函数,然后返回一个vector3,在每个点运行一下这个函数然后加起来即可。