« 不要犹豫了,用sstream吧SwapBuffers的等待,虚伪的FPS »

水效果Ⅲ - 抖动波

这个名字是我私取的。所谓抖动波,是通过在某点“人为地”搅动而产生向四周传播波,如同握绳子某点摇动而产生的物理波。回顾其实现方式,以及把算法移植到shader,让GPU高速运算。——ZwqXin.com

 [水效果Ⅰ - 水池]        [水效果Ⅱ - 涟漪]

本文来源于 ZwqXin (http://www.zwqxin.cn/), 转载请注明
      原文地址:http://www.zwqxin.cn/archives/opengl/water-simulation-3.html

涟漪在物理意义上也就是这么一个抖动波,但是在上篇[水效果Ⅱ - 涟漪]中,涟漪的实现不是采用波的传播方式,而是采用一种类似电场般迅速扑开的“场”,把整个水面纳入计算,根据距波源的距离、时间等模拟出某一时刻的顶点高度。所以说,它体现出传播的特点其实只是波源公式里各个衰减因子的功效而已。对于抖动波,我们就“无法”控制每个顶点的高度,在给予了最初的一个抖动后就应该“任由其发展”。说是最初的抖动,其实不过给予波源点一个向上或向下的速度罢了。假设执行波的传播计算的是RenderWave这么一个每帧执行的函数,它的功能就是在网格中带动一个连锁反应:波源点的上升会带动它所连接的四个相邻点上升,然后这四个相邻点的每一个又带动它的四个相邻点(包括带动它的波源点)……这样就把波传递下去了(传到网格边缘则从对边继续)。之后,再保证波源点具有向上速度的同时具有向下的加速度(即每帧速度都在减小),一个物理波的传播模型便形成了。波源在速度变为0的时候到达最高点并在反向速度的拉力下回落,继续扯动相邻点……

实现的算法的获得应该追溯到一年前,课程三大DEMO之一的完善需要一种简单的海水运动模拟,于是上网找算法找水运动的DEMO,并找到了一个日本人写的这个抖动波算法实现。比较单纯的正弦余弦波结合的方法,这种抖动波能产生更加让人信服的缓波浪。现在再次运用这种算法,本是希望模拟池水轻微的表面浮动,但是结果不理想,一来要等到整个“造浪”过程到达平缓阶段需要好一段时间,如上所述,抖动传播的过程是无法人为干扰的,改变传播速度也没办法,且最初波源中心的突兀而起是很显然的;二来因为没有一个精确的物理式子描述,所以在大波浪运动之下,组成的每个顶点相连并不光滑,造成很多小触点,尽管在海水中这种触点能恰好加强了波的细节描述,但在池水这种水面一般需要很很光滑的场合这样就不好了。

在给出代码之前说一下,如果目的是造成网格各处起伏的波浪的话,波源最好不要位于网格中心,或者网格不要是个正方形。因为波到达边缘要回弹或者从对边继续开始同向拂来,如果网格和波源位置过于“工整”则只会造成波波抵消之类的效果,最初给的抖动(或者说,能量)没办法分散,所以始终会表现为一种重复的运动——波源引导最大振幅的波动。

以下代码是我对原始代码简化后的版本。在DEMO中为了简捷我还是取规正网格和中心波源,没所谓了 - -

  1. //波动开始的时刻
  2. mCurrentWaveDelta[0] = 2.0;//假设0索引是波源点,设定初速
  3.  
  4. //渲染过程:
  5.      for(z = 1; z < mGridScaleZ - 1; ++z)
  6.       for(x = 1; x < mGridScaleX - 1; ++x)
  7.       {
  8.           factor.set(0.0, 0.0, 0.0);
  9.           index = x + z * mGridScaleX;
  10.  
  11.           deltaVec = mOldCurrentOriGrid[index] - mOldCurrentOriGrid[index + 1];
  12.           vecDis = deltaVec.abs() *  NormalLengthPerGridFactor;
  13.           factor += deltaVec.normalize() * (1.0 - vecDis);
  14.  
  15.           deltaVec = mOldCurrentOriGrid[index] - mOldCurrentOriGrid[index - 1];
  16.           vecDis = deltaVec.abs() *  NormalLengthPerGridFactor;
  17.           factor += deltaVec.normalize() * (1.0 - vecDis);
  18.  
  19.           deltaVec = mOldCurrentOriGrid[index] - mOldCurrentOriGrid[index + mGridScaleX];
  20.           vecDis = deltaVec.abs() *  NormalLengthPerGridFactor;
  21.           factor += deltaVec.normalize() * (1.0 - vecDis);
  22.  
  23.           deltaVec = mOldCurrentOriGrid[index] - mOldCurrentOriGrid[index - mGridScaleX];
  24.           vecDis = deltaVec.abs() *  NormalLengthPerGridFactor;
  25.           factor += deltaVec.normalize() * (1.0 - vecDis);
  26.  
  27.           mCurrentWaveDelta[index] += factor * 0.2f * velocity;
  28.  
  29.           mCurrentOriGrid[index] += mCurrentWaveDelta[index];
  30.       }

mOldCurrentOriGrid是用于计算的当前时刻的网格高度,deltaVec是相邻网格顶点之间的向量;NormalLengthPerGridFactor是一小网格的边长之倒数,是为了规范化vecDis到一个与1靠近的值;factor收集了四相邻点的扯动效果,影响该点速度(mCurrentWaveDelta)方向,是正扯反扯就看vecDis与1.0比的大小以及deltaVec方向——看官可以回忆高中学的机械波传播过程,速度,加速度,位置变化等信息。事实上我觉得自己还是不怎么能完全理解的,所以解释的话就如此从简了。

http://ww.zwqxin.com 水效
http://ww.zwqxin.com 水效
http://ww.zwqxin.com 水效

由截图可见波是从波源一直传播开来并扩散的,最后达到算是比较和缓的状态。这就是CPU上实现的抖动波。

接下来是GPU上实现的抖动波。整整折磨了我暑假里不止一个星期啊。

把算法搬到shader上,但是这里每个顶点都要知道相邻点的情况,所以不能交给并行处理顶点的vertex shader来做。方法是:

1.独立设一个shader对象,把全部顶点打包成数组(也就是纹理,见[Vertex Texture Fetch 顶点纹理拾取] ,每个纹素包含一个顶点信息,如果有N*N个顶点,就弄一张N*N大小的纹理好了),传入该fragment shader;

2.把上面的算法应用到这张纹理上(这是难点,就是这里搞死我的就是这里!),注意纹理可以随便检索,因此通过纹理坐标的偏移可以获得相邻像素里的对应的顶点信息,结果(代表每个顶点的位置)作为gl_FragData渲染到应用程序的一张空纹理上(FBO的应用,见[学一学,FBO] );

3.最后是处理网格顶点的shader对象的vertex shader对此处理过的纹理进行VTF(顶点纹理获取,同见[Vertex Texture Fetch 顶点纹理拾取] ),把得到的顶点位置数据作为对应顶点的结果。

当然,编程细节可多了。不可能一一在此阐述,但大体思路就是这样。作为效果演示,以下DEMO的抖动波效果是夸张了点,但是它能让你感性了解什么是抖动波。(众:喂,这个名词不是你自己乱取的咩~)

http://ww.zwqxin.com 水效
http://ww.zwqxin.com 水效
(截图太囧,还是动态的好~)

下篇预告:[水效果Ⅳ - GPU水面波]

DEMO提示:可按提示文字操作,右键是涟漪效果(基于CPU计算的)[注意别连续按];D键是抖动波演示[注意别多按]。

本DEMO用了不少OpenGL技术和GPU技术,所以对显卡的要求颇高的,(另外,ATI显卡目前连VTF都不一定支持,汗~)我只能保证NV 9 series以上大概能正常观看了。

下载点My Google Code - Downloads:   1. Water-Simulation-抖动波
http://code.google.com/p/zwqxindemos/downloads/list

本文来源于 ZwqXin (http://www.zwqxin.cn/), 转载请注明
      原文地址:http://www.zwqxin.cn/archives/opengl/water-simulation-3.html

  • quote 1.XD
  • (另外,ATI显卡目前连VTF都不一定支持,汗~)
    ~ SM30以上的都支持嘛.
  • 2009-10-30 9:09:32 回复该留言
  • quote 3.tianjie
  • 老大,GPU的波什么时候有啊
    zwqxin 于 2011-8-22 22:09:49 回复
    擦,我都忘记这回事了....有时间再更新.
  • 2011-8-22 16:39:00 回复该留言

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

IE下本页面显示有问题?

→点击地址栏右侧【兼容视图】←

日历

Search

网站分类

最新评论及回复

最近发表

Powered By Z-Blog 1.8 Walle Build 100427

Copyright 2008-2024 ZwqXin. All Rights Reserved. Theme edited from ipati.