水波模拟算法
一、 理论依据
水波的物理学模型便是理论依据。水波有如下特性:
扩散:水波总是从被扰动的中心向外扩散。在水波扩散过程中每个点都在得到能量后以自己为中心震动,并向四周传播能量。之所以从干扰点向外扩散,是因为内部的各点能量互相抵消而看不到震荡。重要的是,每个点都在以自己为中心进行震荡,并向四周扩散能量。
衰减:水波在传播过程中能量会逐渐的衰减,因为水的震荡是有阻尼的。
折射:由于水波表面各处有不同程度的倾斜,由于折射,将会看到水底景物的不同程度的偏移,看起来是变形的。观察点正下方的景物由于折射偏移,开起来并不在正下方。
反射:由于水波表面的凹凸不平,比起平静时期的水面,水面上各点反光程度将会不同程度的受到影响,从而改变了自己的亮度,颜色。
水波还有衍射等特性。但是考虑问题的核心在于能量传递或者能量扩散。因为这是该模型的根源。
二、 约束条件
实现该算法的约束条件。
约束条件 | 描述 |
实时渲染 | 由于该算法要实时渲染水波,所以只能近似推导,而且不能使用三角函数、除法等低效运算,以保证速度。 |
|
三、 设计方案
用两个数组来模拟水池。数组大小:水池高度 * 水池宽度。其中一个数组存储水池的上一个状态,另一个用来存储当前用上一个状态正在计算的下一个状态。计算完毕,把新状态渲染出来;然后新的状态就变为“上一个状态”的水池,用它来计算更新的水池状态,数据保存在原来的第一个水池中。两个水池交替的成为新、旧状态池。从而,虽着实间的推移,能量就会被扩散开来。
为了保证执行效率,水波的扩散、折射等均用简化后的模型代替,以使算法成为线性简单的;对于里面的乘法、除法运算尽量采用2的幂,可以通过移位运算快速实现。
四、 数学推理与算法设计
根据以上设计,就可以建立模型进行数学推理和算法设计。上述两个矩形代表了两个数组,即两个水池状态。令水池宽度为W(W=w+1),水池高度为H(H=h+1),假设点A的坐标为 (I,J),那么A点对应的数组下标为:(J * W)+ I 。O点(0,0)对应buf[0],对角线B(w, h)代表buf[W * H – 1],即数组的最后一个元素。
计算波幅的公式推导:如图所示,假设任意一点x0处下一个时刻的能量能根据当前状态下周围的12个点和x0自身的振幅推算出来。即x0在下一个时刻的振幅要由自身的当前振幅和受周围12个点的能量扩散得到。并且假设这12个点影响x0的程度都一样,这个模型已经得到很大的简化。可以得到:
x0’ = a * ( x1 + x2 + x3 + … + x9 + y1 + y2 + y3 + y4 ) + b * x0
a, b为待定系数,x0’为下一个时刻的振幅,其余为当前振幅。
如果假设水的阻尼系数为0,那么能量会守恒。即,所有点的能量总和在前后时刻的状态下保持不变。x0’ + x1’ + … + xn’ = x0 + x1 + … + xn
因为x0受周围12个点的影响,反过来也就是说周围12个点x1, x2, …. y4都要受x0的影响,x0总共要出现12次,其余都一样(忽略边缘)。
那么 ( 12a + b) * x0 + (12a + b) * x1 + ….. + (12a + b) * xn = x0 + x1 + … + xn
推导出:12a + b = 1的结果。
找出一个解: a = 1/4,b = -2,这样1/4可以移位得到,效率很高。
所以得到需要的数学公式:x0’ = (x1 + x2 + … + y4) / 4 – 2 * x0
但是实际上这组解不准确,测试中也出现了问题。因为按照事实,x0点本身对下一个时刻的能量影响不可能大于x0本身,只能等于它本身。所以,只能取b = -1,绝对值为1。这样,得到a = 1/6,
即x0’ = (x1 + x2 + … + y4) /6 – x0
经过测试,这组解效果最好,说明跟实际情况接近。
考虑阻尼:
真正的水是有阻尼的,否则,上面的模型产生的震动将永远进行下去。所以需要考虑一个阻尼,让每个点在后续的时刻能量比理想值有所衰减。
即x0’ = (x1 + x2 + … + y4) /6 – x0 然后有 x0’ = x0 – x0 * 阻尼因子
考虑折射:
要进行精确的计算不现实,因为当前的设计方案无法模拟真正的折射。现在进行线性逼近模拟就可以了。水面越倾斜,折射越厉害,即水下的景物看上去偏移越厉害。所以,用谋点的前后两点的波幅差作为该点的折射偏移量。
对于点x0来说,在x和y两个方向的折射偏移量分别为:
xoff = x7 – x5
yoff = x3 – x1
假设x0点的坐标为(I,J),那么x0点上应该显示的水底景物为 (I + x0ff, J + yoff)位置正下方的景物点。
考虑波源:
为了比较逼真的模拟波源,考虑水面受到扰动的时候并不是一个点上受到了扰动,而是在一个比较小的范围内受到了扰动。所以考虑一个扰动半径R,如果x0点上被扰动,那么以x0为圆心,半径为R的区域内的点都会不同程度的获得能量进行震动。这个能量从圆心向外衰减,即x0点获得最大的能量,距 x0越远的点获得越少的能量。
现在根据下面的图示来进行数学推导近似,以获得比较逼真的扰动效果。假设,圆心为A,距离为d的B点(d < R)获得的能量为:
Eb = Ea – Ea * (R – d) / R = Ea * (1 – d / R)
上面这个简单的近似公式经过测试,能逼真的模拟波源。
考虑光反射:
考虑实际情况,如果水面上出现水波,那么由于水面的凹凸不平,各点对光的反射与平静的水面相比将会发生不同程度的变化。具体与很多因素有关,不可能进行精确的计算。与前面一样,这里用简单化的模型进行线性近似计算。
对于水面上一点x0(I,J),对应到数组buf[J*w + I],该点的能量假定为E0,那么x0点上显示的景物点取得以后,得到颜色分量 r,g,b
现在考虑反射后得到新的颜色分量:
r = r + E0 g = g + E0 b = b + E0
如果超出0—255的范围,则进行修正。这样就得到一个效果,就是根据能量的不同,水面上有些点的亮度得到加强,而有些点的亮度得到削弱,由于能量按照波形分布,最终得到水波的波形效果。
(注:这里的反射只考虑了亮度变化,而没有考虑由于环境光而产生的颜色变化)
一、 验证
在使用了背景图片的情况下,水的折射就可以单独成波,没有考虑反射也能看到效果。因为背景图像按照波能偏移显示,得到水波效果。
在考虑了反射的情况下(无论是否考虑了折射),不论是否使用了背景图像,都会有水波效果,因为水面各点的亮度按照波能进行加强和减弱。当然,两者考虑的情况下水波最为逼真。
这就相当于对一池清水,没有反射光的时候就算有扰动,也看不到波形;而如果有水底背景,只要有折射存在,也能感到有水波存在。形成水波最主要的还是反射因素。