在Android中,每一个图像像素通过一个4字节整数来展现:最高位字节用作alpha通道,接下来的事Red,依次类推,接下来的两个字节对应实现Green和Bule。

要达到现实的水波效果比较难,这里一切从简了。

先复习一下物理学。在一滩平静的水面(所有点的振幅为0),扔上一个半径为r的圆形石头,则第一时间水面上被石头打到的那部分水就会往下沉(振幅变为负)。然后,每一个被打到的点都会把这刚刚获取的能量往四周扩散(在这个例子中,假设只有上下左右四个方向的点受到中心的影响,说了一切从简的),同时,由于扩散的过程当中的能量损失,振幅会变得越来越小,直至整个水面恢复平静。

折射,在一张背景图片模拟水波效果的重点在于模拟水波的折射效果。出现水波的时候,相邻两个点之间的高度不一致,出现了一定的高度差,假定我们从正上方看这个水波,这个高度差就会产生一个折射效果,即我们看到的点应该在实际位置的偏下位置。一切从简的话,这个位置偏移多少就直接由这个高度差来决定算了。就简单模拟一下,其实运行之后的效果也不是那么的差。

一段段代码的分析:

buf1和buf2分别用来存储一个像素点在一次渲染前和渲染后的振幅,BitMap1,BitMap2用来存储获得的图片像素
short[] buf2;short[] buf1;int[] Bitmap2;int[] Bitmap1;buf2 = new short[BACKWIDTH * BACKHEIGHT];buf1 = new short[BACKWIDTH * BACKHEIGHT];Bitmap2 = new int[BACKWIDTH * BACKHEIGHT];Bitmap1 = new int[BACKWIDTH * BACKHEIGHT];

扔石头,如上所述
则第一时间水面上被石头打到的那部分水就会往下沉(振幅变为负)。
void DropStone(int x,// x坐标int y,// y坐标int stonesize,// 波源半径int stoneweight)// 波源能量{for (int posx = x - stonesize; posx < x + stonesize; posx++)for (int posy = y - stonesize; posy < y + stonesize; posy++)if ((posx - x) * (posx - x) + (posy - y) * (posy - y) < stonesize* stonesize)buf1[BACKWIDTH * posy + posx] = (short) -stoneweight;}

这一段则是现实扩散的过程。buf2为扩散之后的振幅,由于只考虑上下左右四个方向对中心振幅的影响,那么影响一个点在一次扩散之后的振幅为四周的振幅和自己上一次的振幅。四周的影响假定相同。设X为中心处得振幅,上下左右振幅为X1,X2,X3,X4,X’为一次扩散之后的振幅则。X’=(X1 +X2+X3+X4)*a+X*b。 这个扩散是相对的,四个方向同样要受到中心点的影响,非常粗虐的计算,根据能量守恒,同时把这个局部当做整体。X+ X1 + X2+ X3 +X4=X'+X1'+X2'+ X3' + X4'。代入之后得出结果4a+b=1。取一组合理的解为a=1/2,b=-1。为了提高效率,将除以2变成移位。得到新的振幅之后,执行衰减。
void RippleSpread() {for (int i = BACKWIDTH; i < BACKWIDTH * BACKHEIGHT - BACKWIDTH; i++) {// 波能扩散buf2[i] = (short) (((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;}

扩散一次之后,则根据相邻像素点之间的高度差(即振幅差)计算出偏移量Xoff和Yoff,上面说过的,偏移就直接等于高度差算了,一切从简。然后将偏移加到具体的像素里头,新的像素为原来的像素加上偏移之后的像素。

/* 渲染你水纹效果 */void render() {int xoff, yoff;int k = BACKWIDTH;for (int i = 1; i < BACKHEIGHT - 1; i++) {for (int j = 0; j < BACKWIDTH; j++) {// 计算偏移量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 = BACKWIDTH * (i + yoff) + (j + xoff);pos2 = BACKWIDTH * i + j;Bitmap2[pos2++] = Bitmap1[pos1++];k++;}}}

最后,把这些加到线程里头,DropStone方法在onKeyUp事件中调用,线程绘图了,每过50ms就扩撒一次,直至水面平静。

public void run() {while (!Thread.currentThread().isInterrupted()) {try {Thread.sleep(50);} catch (InterruptedException e) {Thread.currentThread().interrupt();}RippleSpread();render();// 使用postInvalidate可以直接在线程中更新界面postInvalidate();}}

更多相关文章

  1. Android中dip(dp)与px之间单位转换
  2. dp,dip,sp,px,pt,mm,in区别与作用,getDimensionPixelSize,getDime
  3. android获取设备分辨率的新方法
  4. Android单位区别与转化
  5. 如何实现android程序的多屏适配
  6. Android知识体系总结之实战经验部分之Android屏幕适配篇
  7. android 显示16色的图片:输入用颜色矩阵,显示对应的16色位图
  8. android学习01
  9. 今日头条屏幕适配_工具类

随机推荐

  1. Yii 访问 Gii(脚手架)时出现 403 错误
  2. MySQL将IP存储为int以获得更高的性能? [重
  3. ThinkPhp框架 分页 和session验证的使用
  4. PHP之网络编程
  5. 迭代文件夹中的CSV文件,并使用PHP将数据加
  6. PHP安装pdo_mysql的扩展
  7. (高分)我的win2000+php+Apache设置完后,默
  8. 如何扩展这个简单的DataMapper?
  9. php实现无限级分类的树形结构
  10. MySQL / PHP:将整行推入关联数组,_without_