水波模拟算法 - Go语言中文社区

水波模拟算法


 

一、    理论依据

水波的物理学模型便是理论依据。水波有如下特性:

扩散:水波总是从被扰动的中心向外扩散。在水波扩散过程中每个点都在得到能量后以自己为中心震动,并向四周传播能量。之所以从干扰点向外扩散,是因为内部的各点能量互相抵消而看不到震荡。重要的是,每个点都在以自己为中心进行震荡,并向四周扩散能量。

衰减:水波在传播过程中能量会逐渐的衰减,因为水的震荡是有阻尼的。

折射:由于水波表面各处有不同程度的倾斜,由于折射,将会看到水底景物的不同程度的偏移,看起来是变形的。观察点正下方的景物由于折射偏移,开起来并不在正下方。

反射:由于水波表面的凹凸不平,比起平静时期的水面,水面上各点反光程度将会不同程度的受到影响,从而改变了自己的亮度,颜色。

水波还有衍射等特性。但是考虑问题的核心在于能量传递或者能量扩散。因为这是该模型的根源。

二、    约束条件

实现该算法的约束条件。

约束条件

描述

实时渲染

由于该算法要实时渲染水波,所以只能近似推导,而且不能使用三角函数、除法等低效运算,以保证速度。

 


三、    设计方案

用两个数组来模拟水池。数组大小:水池高度 * 水池宽度。其中一个数组存储水池的上一个状态,另一个用来存储当前用上一个状态正在计算的下一个状态。计算完毕,把新状态渲染出来;然后新的状态就变为“上一个状态”的水池,用它来计算更新的水池状态,数据保存在原来的第一个水池中。两个水池交替的成为新、旧状态池。从而,虽着实间的推移,能量就会被扩散开来。

为了保证执行效率,水波的扩散、折射等均用简化后的模型代替,以使算法成为线性简单的;对于里面的乘法、除法运算尽量采用2的幂,可以通过移位运算快速实现。

四、    数学推理与算法设计

根据以上设计,就可以建立模型进行数学推理和算法设计。

 

上述两个矩形代表了两个数组,即两个水池状态。令水池宽度为W(W=w+1),水池高度为H(H=h+1),假设点A的坐标为 IJ),那么A点对应的数组下标为:(J * W+ I O点(00)对应buf[0],对角线Bw, 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/4b = -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来说,在xy两个方向的折射偏移量分别为:

xoff = x7 – x5

yoff = x3 – x1

假设x0点的坐标为(IJ),那么x0点上应该显示的水底景物为 (I + x0ff J + yoff)位置正下方的景物点。

考虑波源:

为了比较逼真的模拟波源,考虑水面受到扰动的时候并不是一个点上受到了扰动,而是在一个比较小的范围内受到了扰动。所以考虑一个扰动半径R,如果x0点上被扰动,那么以x0为圆心,半径为R的区域内的点都会不同程度的获得能量进行震动。这个能量从圆心向外衰减,即x0点获得最大的能量,距 x0越远的点获得越少的能量。

现在根据下面的图示来进行数学推导近似,以获得比较逼真的扰动效果。
 

假设,圆心为A,距离为dB点(d < R)获得的能量为:

Eb = Ea – Ea * (R – d) / R = Ea * (1 – d / R)

上面这个简单的近似公式经过测试,能逼真的模拟波源。

考虑光反射:

考虑实际情况,如果水面上出现水波,那么由于水面的凹凸不平,各点对光的反射与平静的水面相比将会发生不同程度的变化。具体与很多因素有关,不可能进行精确的计算。与前面一样,这里用简单化的模型进行线性近似计算。

对于水面上一点x0IJ),对应到数组buf[J*w + I],该点的能量假定为E0,那么x0点上显示的景物点取得以后,得到颜色分量 rgb

现在考虑反射后得到新的颜色分量:

r = r + E0                   g = g + E0                 b = b + E0

如果超出0—255的范围,则进行修正。这样就得到一个效果,就是根据能量的不同,水面上有些点的亮度得到加强,而有些点的亮度得到削弱,由于能量按照波形分布,最终得到水波的波形效果。

(注:这里的反射只考虑了亮度变化,而没有考虑由于环境光而产生的颜色变化)

一、    验证

在使用了背景图片的情况下,水的折射就可以单独成波,没有考虑反射也能看到效果。因为背景图像按照波能偏移显示,得到水波效果。

在考虑了反射的情况下(无论是否考虑了折射),不论是否使用了背景图像,都会有水波效果,因为水面各点的亮度按照波能进行加强和减弱。当然,两者考虑的情况下水波最为逼真。

这就相当于对一池清水,没有反射光的时候就算有扰动,也看不到波形;而如果有水底背景,只要有折射存在,也能感到有水波存在。形成水波最主要的还是反射因素。


转载于:https://www.cnblogs.com/worldreason/archive/2008/05/09/1189648.html

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_33849215/article/details/94503067
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-03-07 21:18:53
  • 阅读 ( 900 )
  • 分类:算法

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢