标签:
水波纹,鼠标点击后水会四散,产生涟漪的感觉,十分真实.
实现原理:
扩散:当你投一块石头到水中,你会看到一个以石头入水点为圆心所形成的一圈圈的水波,这里,你可能会被这个现象所误导,以为水波上的每一点都是以石头入水点为中心向外扩散的,这是错误的。实际上,水波上的任何一点在任何时候都是以自己为圆心向四周扩散的,之所以会形成一个环状的水波,是因为水波的内部因为扩散的对称而相互
抵消了。
衰减:因为水是有阻尼的,否则,当你在水池中投入石头,水波就会永不停止的震荡下去。
水的折射:因为水波上不同地点的倾斜角度不同,所以,因为水的折射,我们从观察点垂直往下看到的水底并不是在观
察点的正下方,而有一定的偏移。如果不考虑水面上部的光线反射,这就是我们能感觉到水波形状的原因。
反射:水波遇到障碍物会反射。
衍射:如果能在水池中央放上一块礁石,或放一个中间有缝的隔板,那么就能看到水波的衍射现象了
多说无益,看一下效果图
这样子看起来还是极好的.
我先要定义两个波能缓冲区
int *m_pWaveBuf1;
//波能缓冲区1
int *m_pWaveBuf2;
//波能缓冲区2
在初始化函数里,波能缓冲区水池图象一样大小的数组
//获取位图宽、高、一行的字节数 m_iBmpWidth = stBitmap.bmWidth; m_iBmpHeight = stBitmap.bmHeight; //分配波能缓冲区 m_pWaveBuf1 = new int[m_iBmpWidth*m_iBmpHeight]; m_pWaveBuf2 = new int[m_iBmpWidth*m_iBmpHeight];
void CRipple::WaveSpread() { int *lpWave1 = m_pWaveBuf1; int *lpWave2 = m_pWaveBuf2; for(int i = m_iBmpWidth; i < (m_iBmpHeight - 1)*m_iBmpWidth; i++) { //波能扩散 lpWave2[i] = ((lpWave1[i - 1] + lpWave1[i - m_iBmpWidth] + lpWave1[i + 1] + lpWave1[i + m_iBmpWidth]) >> 1) - lpWave2[i]; //波能衰减 lpWave2[i] -= (lpWave2[i] >> 5); } //交换缓冲区 m_pWaveBuf1 = lpWave2; m_pWaveBuf2 = lpWave1; }
某一时刻,X0点的振幅除了受X0点自身振幅的影响外,同时受来自它周围前、后、左、右四个点(X1、X2、X3、X4)
的影响.这四个点对a0点的影响力可以说是机会均等的。那么我们可以假设这个一次公式为:
X0’=a(X1+X2+X3+X4)+bX0 (公式1)
最终我们得到这个函数
X0’=(X1+X2+X3+X4)/
2- X0
水在实际中是存在阻尼的,否则,用上面这个公式,一旦你在水中增加一个波源,水面将永不停止的震荡下去。所以,
还需要对波幅数据进行衰减处理,让每一个点在经过一次计算后,波幅都比理想值按一定的比例降低。这个衰减率经过
测试,用1/32比较合适,也就是1/2^5。可以通过移位运算很快的获得。
在程序中,用一个页面装载原始的图象,用另外一个页面来进行渲染。取得指向页面内存区的指针,然后用根据偏
移量将原始图象上的每一个象素复制到渲染页面上。进行页面渲染.
void CRipple::WaveRender() { int iPtrSource = 0; int iPtrRender = 0; int lineIndex = m_iBmpWidth; int iPosX = 0; int iPosY = 0; //扫描位图 for(int y = 1; y < m_iBmpHeight - 1; y++) { for(int x = 0; x < m_iBmpWidth; x++) { //根据波幅计算位图数据偏移值,渲染点(x,y)对应原始图片(iPosX,iPosY) // iPosX = x + (m_pWaveBuf1[lineIndex - 1] - m_pWaveBuf1[lineIndex + 1]); // iPosY = y + (m_pWaveBuf1[lineIndex - m_iBmpWidth] - m_pWaveBuf1[lineIndex + m_iBmpWidth]); //另外一种计算偏移的方法 int waveData = (1024 - m_pWaveBuf1[lineIndex]); iPosX = (x - m_iBmpWidth/2)*waveData/1024 + m_iBmpWidth/2; iPosY = (y - m_iBmpHeight/2)*waveData/1024 + m_iBmpHeight/2; if(0 <= iPosX && iPosX < m_iBmpWidth && 0 <= iPosY && iPosY < m_iBmpHeight) { //分别计算原始位图(iPosX,iPosY)和渲染位图(x,y)对应的起始位图数据 iPtrSource = iPosY*m_iBytesPerWidth + iPosX*3; iPtrRender = y*m_iBytesPerWidth + x*3; //渲染位图,重新打点数据 for(int c = 0; c < 3; c++) { m_pBmpRender[iPtrRender + c] = m_pBmpSource[iPtrSource + c]; } } lineIndex++; } } //设置渲染后的位图 SetDIBits(m_hRenderDC, m_hRenderBmp, 0, m_iBmpHeight, m_pBmpRender, &m_stBitmapInfo, DIB_RGB_COLORS); }
void CRipple::DropStone(int x, int y, int stoneSize, int stoneWeight) { int posX = 0; int posY = 0; for(int i = -stoneSize; i < stoneSize; i++) { for(int j = -stoneSize; j < stoneSize; j++) { posX = x + i; posY = y + j; //控制范围,不能超出图片 if(posX < 0 || posX >= m_iBmpWidth || posY < 0 || posY >= m_iBmpHeight) { continue; } //在一个圆形区域内,初始化波能缓冲区1 if(i*i + j*j <= stoneSize*stoneSize) { m_pWaveBuf1[posY*m_iBmpWidth + posX] = stoneWeight; } } } }
本文参考 http://blog.csdn.net/ghj1976/article/details/3071
标签:
原文地址:http://blog.csdn.net/xiaoge132/article/details/51346854