Ripple [水波] 程序实现水波效果 水纹特效 算法

时间:2019-05-14 12:40:09下载本文作者:会员上传
简介:写写帮文库小编为你整理了多篇相关的《Ripple [水波] 程序实现水波效果 水纹特效 算法》,但愿对你工作学习有帮助,当然你在写写帮文库还可以找到更多《Ripple [水波] 程序实现水波效果 水纹特效 算法》。

第一篇:Ripple [水波] 程序实现水波效果 水纹特效 算法

看到左边这幅动画(如果没有出现,请耐心的稍等片刻),你也许不会相信它其实是用电脑做出来的,这就是“水波”特效的魅力所在。

在介绍编程之前,先让我们来回顾一下在高中的物理课上我们所学的关于水波的知识。水波有如下几个特性:

 扩散:当你投一块石头到水中,你会看到一个以石头入水点为圆心所形成的一圈圈的水波,这里,你可能会被这个现象所误导,以为水波上的每一点都是以石头入水点为中心向外扩散的,这是错误的。实际上,水波上的任何一点在任何时候都是以自己为圆心向四周扩散的,之所以会形成一个环状的水波,是因为水波的内部因为扩散的对称而相互抵消了。

  衰减:因为水是有阻尼的,否则,当你在水池中投入石头,水波就会永不停止的震荡下去。

水的折射:因为水波上不同地点的倾斜角度不同,所以,因为水的折射,我们从观察点垂直往下看到的水底并不是在观察点的正下方,而有一定的偏移。如果不考虑水面上部的光线反射,这就是我们能感觉到水波形状的原因。

  反射:水波遇到障碍物会反射。

衍射:忽然又想到这一点,但是在程序里却看不到,如果能在水池中央放上一块礁石,或放一个中间有缝的隔板,那么就能看到水波的衍射现象了。

好了,有了这几个特性,再运用数学和几何知识,我们就可以模拟出真实的水波了。但是,如果你曾用3DMax做过水波的动画,你就会知道要渲染出一幅真实形状的水波画面少说也得好几十秒,而我们现在需要的是实时的渲染,每秒种至少也得渲染20帧才能使得水波得以平滑的显示。考虑到电脑运算的速度,我们不可能按照正弦函数或精确的公式来构造水波,不能用乘除法,更不能用sin、cos,只能用一种取近似值的快速算法,尽管这种算法存在一定误差,但是为了满足实时动画的要求,我们不得不这样做。

首先我们要建立两个与水池图象一样大小的数组buf1[PoolWidth*PoolHeight]和buf2[PoolWidth*PoolHeight](PoolWidth=水池图象的象素宽度、PoolHeight=水池图象的象素高度),用来保存水面上每一个点的前一时刻和后一时刻波幅数据,因为波幅也就代表了波的能量,所以以后我们称这两个数组为波能缓冲区。水面在初始状态时是一个平面,各点的波幅都为0,所以,这两个数组的初始值都等于0。下面来推导计算波幅的公式

我们假设存在这样一个一次公式,可以在任意时刻根据某一个点周围前、后、左、右四个点以及该点自身的振幅来推算出下一时刻该点的振幅,那么,我们就有可能用归纳法求出任意时刻这个水面上任意一点的振幅。如左图,你可以看到,某一时刻,X0点的振幅除了受X0点自身振幅的影响外,同时受来自它周围前、后、左、右四个点(X1、X2、X3、X4)的影响(为了简化,我们忽略了其它所有点),而且,这四个点对a0点的影响力可以说是机会均等的。那么我们可以假设这个一次公式为: X0'=a(X1+X2+X3+X4)+bX0(公式1)a、b为待定系数,X0'为0点下一时刻的振幅

X0、X1、X2、X3、X4为当前时刻的振幅 下面我们来求解a和b。

假设水的阻尼为0。在这种理想条件下,水的总势能将保持不变。也就是说在任何时刻,所有点的振幅的和保持不变。那么可以得到下面这个公式: X0'+X1'+...+Xn' = X0+X1+...+Xn

将每一个点都象公式1那样计算,然后代入上式,得到:(4a+b)X0+(4a+b)X1+...(4a+b)Xn = X0+X1+...+Xn =>4a+b=1

找出一个最简解:a = 1/

2、b =-1

因为1/2可以用移位运算符“>>”来进行,不用进行乘除法,所以,这组解是最适用的而且是最快的。那么最后得到的公式就是: X0'=(X1+X2+X3+X4)/ 2-X0

好了,有了上面这个近似公式,你就可以推广到下面这个一般结论:已知某一时刻水面上任意一点的波幅,那么,在下一时刻,任意一点的波幅就等于与该点紧邻的前、后、左、右四点的波幅的和除以

2、再减去该点的波幅。

应该注意到,水在实际中是存在阻尼的,否则,用上面这个公式,一旦你在水中增加一个波源,水面将永不停止的震荡下去。所以,还需要对波幅数据进行衰减处理,让每一个点在经过一次计算后,波幅都比理想值按一定的比例降低。这个衰减率经过测试,用1/32比较合适,也就是1/2^5。可以通过移位运算很快的获得。

到这里,水波特效制作中最艰难的部分已经度过了,下面是源程序中计算波幅数据的代码。//******************************************************* //计算波能数据缓冲区

//******************************************************* void RippleSpread(){ for(int i=BACKWIDTH;i

buf2[i] =((buf1[i-1]+ buf1[i+1]+ buf1[i-BACKWIDTH]+ buf1[i+BACKWIDTH])>>1)-buf2[i];//波能衰减

buf2[i]-= buf2[i]>>5;}

//交换波能数据缓冲区

short *ptmp =buf1;buf1 = buf2;buf2 = ptmp;} 写到这里,我已经两眼发晕了,呼呼——,先休息一下......好了,下面再来根据算出的波幅数据对页面进行渲染。

因为水的折射,当水面不与我们的视线相垂直的时候,我们所看到的水下的景物并不是在观察点的正下方,而存在一定的偏移。偏移的程度与水波的斜率,水的折射率和水的深度都有关系,如果要进行精确的计算的话,显然是很不现实的。同样,我们只需要做线形的近似处理就行了。因为水面越倾斜,所看到的水下景物偏移量就越大,所以,我们可以近似的用水面上某点的前后、左右两点的波幅之差来代表所看到水底景物的偏移量。

在程序中,用一个页面装载原始的图象,用另外一个页面来进行渲染。先用Lock函数锁定两个页面,取得指向页面内存区的指针,然后用根据偏移量将原始图象上的每一个象素复制到渲染页面上。进行页面渲染的代码如下:(下面的代码为了便于理解,并没有进行优化,实际上,优化后的代码比它要麻烦许多)

//******************************************************* //根据波能数据缓冲区对离屏页面进行渲染

//******************************************************* void RenderRipple(){ //锁定两个离屏页面

DDSURFACEDESC ddsd1, ddsd2;ddsd1.dwSize = sizeof(DDSURFACEDESC);ddsd2.dwSize = sizeof(DDSURFACEDESC);lpDDSPic1->Lock(NULL, &ddsd1, DDLOCK_WAIT, NULL);lpDDSPic2->Lock(NULL, &ddsd2, DDLOCK_WAIT, NULL);

//取得页面象素位深度,和页面内存指针

int depth=ddsd1.ddpfPixelFormat.dwRGBBitCount/8;BYTE *Bitmap1 =(BYTE*)ddsd1.lpSurface;BYTE *Bitmap2 =(BYTE*)ddsd2.lpSurface;

//下面进行页面渲染 int xoff, yoff;int k = BACKWIDTH;for(int i=1;i

xoff = buf1[k-1]-buf1[k+1];yoff = buf1[k-BACKWIDTH]-buf1[k+BACKWIDTH];

//判断坐标是否在窗口范围内

if((i+yoff)< 0){k++;continue;} if((i+yoff)> BACKHEIGHT){k++;continue;} if((j+xoff)< 0){k++;continue;} if((j+xoff)> BACKWIDTH){k++;continue;}

//计算出偏移象素和原始象素的内存地址偏移量

int pos1, pos2;pos1=ddsd1.lPitch*(i+yoff)+ depth*(j+xoff);pos2=ddsd2.lPitch*i+ depth*j;

//复制象素

for(int d=0;d

lpDDSPic1->Unlock(&ddsd1);lpDDSPic2->Unlock(&ddsd2);} 增加波源

俗话说:无风不起浪,为了形成水波,我们必须在水池中加入波源,你可以想象成向水中投入石头,形成的波源的大小和能量与石头的半径和你扔石头的力量都有关系。知道了这些,那么好,我们只要修改波能数据缓冲区buf,让它在石头入水的地点来一个负的“尖脉冲”,即让buf[x,y]=-n。经过实验,n的范围在(32~128)之间比较合适。

控制波源半径也好办,你只要以石头入水中心点为圆心,画一个以石头半径为半径的圆,让这个圆中所有的点都来这么一个负的“尖脉冲”就可以了(这里也做了近似处理)。增加波源的代码如下: //***************************************************** //增加波源

//***************************************************** void DropStone(int x,//x坐标

int y,//y坐标

int stonesize,//波源半径

int stoneweight)//波源能量 { //判断坐标是否在屏幕范围内

if((x+stonesize)>BACKWIDTH ||(y+stonesize)>BACKHEIGHT||(x-stonesize)<0||(y-stonesize)<0)return;

for(int posx=x-stonesize;posx

好了,至此,水波特效的制作原理就此就全部揭示了。在上面的推导中,每一步都进行了很多看似非常过分的近似处理,但是,你完全不必担心,事实证明,用这种方法,在速度和图象上都可以获得非常好的效果。源程序中有非常详尽的注释,仔细推敲一下,看懂它们应该不成问题。这个程序是Win32下的DirectX编程,没有使用任何包装库。在我的电脑上(AMDK6-200、2MVRam、64MSRam),320x240的画面大小,每秒可以达到25帧。与前几个程序不一样,这个程序使用了窗口模式,所以调试起来很方便。如果你对窗口模式编程不熟悉,这个程序也是一个很好的例子。

这种用数据缓冲区对图象进行水波处理的方法,有个最大的好处就是,程序运算和其示的速度与水波的复杂程度是没有关系的,无论水面是风平浪静还是波涛汹涌,程序的fps始终保持不变,这一点你研究一下程序就应该可以看出来。实际上,如果你掌握了这种方法,将这种方法推广一下,完全可以做出另外一些特殊的效果,如烟雾、大气、阳光等,我现在也正在研究这些特效的制作,相信不久以后就会有新的收获。

http://hi.baidu.com/hackcsy/blog/item/ec6a64364c31f9daa2cc2b6e.html

第二篇:编译原理课程设计报告---集合LASTVT(P)构造算法的程序实现

计算机与信息学院 编译原理课程设计 实验报告

级 学生姓名及学号 课程教学班号 任

师 实验指导教师 实验地

学年第学期

设计目的及要求:

集合LASTVT(P)构造算法的程序实现

设计内容及要求:构造一程序,实现教材P.91的LASTVT(P)集合的构造算法。对任一给定的算符文法G,程序输出所有非终结符P的LASTVT(P)。

设计内容:

实现教材上的算法,对于任意给定的算符文法,输出算符文法中所有的非终结符P的LASTV(P);

主要算法描述:

对于输入的文法,使用一个char型二维数组进行存储,依次对每个非终结符求LASTVT 集。

输入输出形式:

输入: 程序运行后从控制台输入算符文法,要指定输入的文法规则数目,且形式与教材文法相同。

输出:在控制台输出每个非终结符的LASTVT集,且将带有‘|’的文法转换成多个文法。

总结:

本次课程设计我借鉴了第四学期编译原理课程的课程实验,通过本次课程设计我对编译原理课程的相关内容有了复习和巩固,对当时没有弄清楚的问题有了更深的认识,更加掌握了LASTVT集的生成原理,帮助我更好地理解了算符优先分析算法。程序运行结果:

程序源码:

#include #include #include usingnamespace std;

char lable[20];//文法终极符集

char String[20][10];//用于输入串的分析

int r;//文法规则个数

int r1;//转化后文法规则个数

char st[10][30];//用来存储文法规则

char last[10][10];//文法非终结符LASTVT集

int lflag[10] = { 0 };//标志第i个非终结符的LASTVT集是否已求出

//判断是否是终结符

int zhongjie(charc)//判断字符c是否是终极符 {

}

//求lastvt集

void lastvt(charc)//求LASTVT集 {

int i, j, k, m, n;for(i = 0;i

} return 0;if(c == lable[i])return 1;

} if(lflag[i] == 0){

n = last[i][0] + 1;m = 0;do {

if(st[i][m + 1] == '' || st[i][m + 1] == '|'){

if(zhongjie(st[i][m])){

} else {

if(zhongjie(st[i][m1];n++;last[i][n] = st[i][m];n++;

}

}

}

}

}

}

} m++;} while(st[i][m]!= '');last[i][n] = '';last[i][0] =--n;lflag[i] = 1;//转换 “|”

void transform()// 转换 带“|”的文法

{

for(i = 0;i

text[x][y] = st[i][0];y++;for(j = 1;st[i][j]!= '';j++){

if(st[i][j] == '|'){

} else { text[x][y] = st[i][j];y++;text[x][y] = '';x++;y = 0;text[x][y] = st[i][0];y++;text[x][y++] = '-';text[x][y++] = '>';char text[20][10];int i, j, l, x = 0, y = 0;x = 0;

}

} } } text[x][y] = '';x++;y = 0;r1 = x;printf(“转化后的文法为:n”);for(i = 0;i

{

} String[i][0] = text[i][0];for(j = 3, l = 1;text[i][j]!= '';j++, l++)String[i][l] = text[i][j];String[i][l] = '';

后的转化文法)*/ printf(“%sn”, text[i]);//每个非终结符求lastvt void table2(){

}

//判断输入是文法是否规范 void judge(){

int i, j, k = 0;printf(“请输入文法规则数:”);scanf(“%d”, &r);printf(“请输入文法规则:n”);for(i = 0;i

for(int i = 0;i

}

}

/*last[i][0]表示LASTVT集中元素的个数*/

last[i][0] = 0;for(i = 0;i

} for(i = 0;i

} for(j = 0;st[i][j]!= '';j++){

} if((st[i][j]<'A' || st[i][j]>'Z')&& st[i][j]!= '-'&&st[i][j]!= lable[k++] = st[i][j];for(j = 0;st[i][j]!= '';j++){

} if(st[i][0]<'A' || st[i][0]>'Z'){

} if(st[i][j] >= 'A'&&st[i][j] <= 'Z'){

} if(st[i][j + 1] >= 'A'&&st[i][j + 1] <= 'Z'){

} printf(“不是算符文法!n”);exit(-1);printf(“不是算符文法!n”);exit(-1);'>'&&st[i][j]!= '|')//输出结果 void output(){

printf(“每个非终结符的LASTVT集为:n”);//输出每个非终结符的LASTVT集 for(i = 0;i

} {

} printf(“%c: ”, st[i][0]);for(j = 0;j

}

void main(){

} int i = 1;while(i == 1){

} judge();transform();table2();output();initalize();memset(lable, 0, sizeof(lable));memset(String, 0, sizeof(String));memset(st,0,sizeof(st));memset(last, 0, sizeof(last));memset(lflag, 0, sizeof(lflag));

下载Ripple [水波] 程序实现水波效果 水纹特效 算法word格式文档
下载Ripple [水波] 程序实现水波效果 水纹特效 算法.doc
将本文档下载到自己电脑,方便修改和收藏,请勿使用迅雷等下载。
点此处下载文档

文档为doc格式


声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:645879355@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。

相关范文推荐