分享
 
 
 

借助DirectDraw实现对水波的计算机模拟

王朝other·作者佚名  2008-05-19
窄屏简体版  字體: |||超大  

目前,各种高性能计算机正以其强大的运算能力被广泛应用于各种领域,其中对自然界的物理现象和自然规律进行仿真是主要应用之一。由于许多专业书籍对此类仿真技术讳莫如深,使不少程序设计人员对此类程序的设计问题感到无从下手。本文以对真实水波的产生、扩散、衰减以及多个水波的交迭过程的计算机模拟为例,介绍此类程序的设计思路与解决方法。在程序的实现过程中,为了使仿真的效果更加逼真、处理数据显示的速度更快,本文使用了DirectX中的DirectDraw技术利用硬件加速器对数据的显示进行加速。

扩散及衰减处理

要对某种自然现象进行仿真,就必须对该现象的特性有很好的认识。比如对于本文所仿真的对象水波而言,就要对水波的诸多特性,如扩散性、衰减性、反射性以及水的折射等都要有所认识,并最终通过程序算法体现在程序中。这些关于波的特性属于普通物理的研究范畴,本文不再赘述。根据以上的特性,再利用计算机、数学和几何等有关知识就可以在计算机上模拟出真实的水波了。

由于在模拟时需要的是实时的渲染,所以每秒种至少要渲染15帧以上的画面才能使水波平滑地显示。考虑到普通计算机的运算速度较慢,所以不能用乘、除法,更不可以使用正、余弦函数以精确的公式来构造水波,只能通过使用简单而高速的加、减法的近似算法来实现。

首先,可以用两个与水池图像一样大小的数组buf1和buf2来保存水面上每一个点(对应于每一个像素的离散化点)的前、后两个时刻的波幅数据。在无外力干扰的稳定状态下,水面是一个平面,水面各点的波幅都为0。当有外力干扰时,如向水池投一颗石子会使水面泛起层层的涟漪,实际上并非水面上的点在向外扩散,而是仍停在原地上下移动,由于振动幅度的变化而引起视觉上的变化。由于水波上的任何一点在任何时候都是通过振幅的变化把能量以自己为中心向四周扩散,所以可以近似认为一个点只会对相邻的前、后、左、右4个点有影响。这样我们就可以用归纳法来根据任一点在某时刻周围4点的振幅来求出该点在下一时刻的振动幅度。假设表示该关系的公式为:

A0’=a×(A1+A2+A3+A4)+b×A0 (公式1)

其中a、b为待定系数,A0’为0点下一时刻的振幅,A0、A1、A2、A3、A4均为当前时刻周围各点振幅。在不考虑衰减情况下波的能量守恒,即上下时刻各点振幅之和守恒,这可以用公式2表示:

A0’+A1’+...+An’= A0+A1+...+An(公式2)

将公式1代入公式2:

(4a+b)×A0+(4a+b)×A1+...(4a+b)×An = A0+A1+...+An

化简公式可得4a+b=1, 取a = 1/2、b = -1时可以满足条件,而且除以2可以用运算速度很快的移位运算符“”来进行。这样,向公式1中代入系数可得到无阻力状态下的周围4点对中心点的影响关系式:

A0’=(A1+A2+A3+A4)/ 2- A0

因此,水面上下一时刻任意一点的波幅等于与该点紧邻的前、后、左、右4点的波幅之和的一半与该点在上一时刻的波幅之差。但在实际中水是存在阻力的,水波会在扩散过程中逐渐衰减直至消失。所以,还要对波幅数据进行衰减处理,让每一个点在经过一次运算后,波幅按一定的比例衰减。笔者实验发现,经验系数(衰减率)取1/32比较合适,同时它也可以通过移位运算很快地获得。下面是具体计算波幅数据的主要代码:

void Spread()

{

……

for(int i=BACKWIDTH;i

/td

{

//能量的扩散

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;

……

}

光折射模拟

虽然模拟了对波的传播过程,但如不考虑起伏的水波对光的折射也是不逼真的。根据光学有关知识,我们所看到的水下的景物并非在观察点的正下方,而是存在一定的偏移。偏移的程度同水波的斜率、水的折射率和水的深度都有关系,出于对处理速度的考虑同样也不能对其进行精确的模拟,只能做线性的近似处理。因为水面越倾斜,所看到的水下景物偏移量就越大,所以,我们可以近似地用水面上某点的前后、左右两点的波幅之差来代表所看到的水底景物的偏移量:

void Render()

{

……

int xoff, yoff;

int k = BACKWIDTH;

for (int i=1; i

{

for (int j=0; j

{

//计算偏移量

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

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

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

if ((i+yoff )

if ((i+yoff ) BACKHEIGHT) {k++; continue;}

if ((j+xoff )

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

/td

Bitmap2[pos2++]=Bitmap1[pos1++];

k++;

}

}

……

}

生成波源

在无外力影响的情况下,水面是不会自发产生水波的,必须对水面施加某种波源才能引起水波的扩散。扩散的速度与范围同波源的能量大小与受力范围有关。我们可以通过在程序中人为地修改振幅缓冲区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)

return;

……

for (int posx=x-stonesize; posx

for (int posy=y-stonesize; posy

if ((posx-x)*(posx-x) + (posy-y)*(posy-y)

buf1[BACKWIDTH*posy+posx] = -stoneweight;

……

}

虽然在上述的推导中多处采用了看似过分的近似处理,但是完全不必担心效果,事实证明,用这种方法,在速度和图像上都可以获得非常好的效果。图1就是从其中截取的一帧画面,很逼真地再现了水波的产生过程。

水波模拟

这种用数据缓冲区对图像进行处理的方法的最大的好处就是:程序运算和显示的速度与水波的复杂程度无关,用类似的方法完全可以对其他一些物理和自然现象,如烟雾、云彩、阳光等,进行逼真的模拟。

加速显示

由于动画对处理速度要求比较严格,所以要竭尽所能来提高数据的处理速度,不仅在算法上如此,在显示上更是如此。普通的GDI函数的处理速度在此类计算中是无法容忍的,为此,笔者采用了DirectX中的DirectDraw技术来对图形进行加速处理。DirectDraw是DirectX SDK中的一员,也是其中最主要的一个部件。它允许程序员直接操作显存、硬件位图映射以及硬件覆盖和换页技术。它在提供直接访问显示设备的同时,与GDI相兼容,提供了一种与设备无关的途径,以访问特定的显示设备的某些高级特性。

本文例子采用Microsoft SDK编码。首先,对DirectDraw环境进行初始化设置,对各页面进行创建和初始化:

BOOL InitDDraw(void)

{

DDSURFACEDESC ddsd;

HRESULT ddrval;

……

//创建DirectDraw对象

ddrval = DirectDrawCreate( NULL, &lpDD, NULL );

……

//取得全屏独占模式

ddrval = lpDD-SetCooperativeLevel(hwndful, DDSCL_EXCLUSIVE | DSCL_FULLSCREEN );

……

//设置显示器显示模式

ddrval = lpDD-SetDisplayMode( DISPLAYMODEWIDTH,DISPLAYMODEHEIGHT, 24);

……

//填充主页面信息

ddsd.dwSize = sizeof( ddsd );

ddsd.dwFlags = DDSD_CAPS ;

ddsd.ddsCaps.dwCaps=

DDSCAPS_PRIMARYSURFACE;

//创建主页面对象

ddrval = lpDD-CreateSurface( &ddsd, &lpDDSPrimary, NULL );

……

ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT |DDSD_WIDTH;

ddsd.ddsCaps.dwCaps=

DDSCAPS_OFFSCREENPLAIN |

DDSCAPS_SYSTEMMEMORY ;

……

lpDD-CreateSurface(&ddsd, &lpDDSPic1, NULL) ;

……

lpDD-CreateSurface(&ddsd, &lpDDSPic2, NULL);

……

lpDD-CreateClipper(0, &lpClipper, NULL);

lpClipper-SetHWnd(0, hwndful);

lpDDSPrimary-SetClipper(lpClipper);

……

//初始化页面图像

DDReLoadBitmap(lpDDSPic1, MAKEINTRESOURCE(IDB_BITMAP1));

DDReLoadBitmap(lpDDSPrimary,MAKEINTRESOURCE(IDB_BITMAP2));

……

ZeroMemory(buf1,

BACKWIDTH*BACKHEIGHT*sizeof(short));

ZeroMemory(buf2,

BACKWIDTH*BACKHEIGHT*sizeof(short));

……

return TRUE;

}

由于必须在应用程序处于激活状态时不断地进行更新,方可完成对整个过程的模拟,所以对数据的实时处理及对页面的渲染在更新帧的函数中完成,最重要

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有