Android 實現水波紋

來源:互聯網
上載者:User

        在Android中,每一個映像像素通過一個4位元組整數來展現:最高位位元組用作alpha通道,接下來的事Red,依次類推,接下來的兩個位元組對應實現Green和Bule。

       要達到現實的水波效果比較難,這裡一切從簡了。

       先複習一下物理學。在一灘平靜的水面(所有點的振幅為0),扔上一個半徑為r的圓形石頭,則第一時間水面上被石頭打到的那部分水就會往下沉(振幅變為負)。然後,每一個被打到的點都會把這剛剛擷取的能量往四周擴散(在這個例子中,假設只有上下左右四個方向的點受到中心的影響,說了一切從簡的),同時,由於擴散的過程當中的能量損失,振幅會變得越來越小,直至整個水面恢複平靜。

       折射,在一張背景圖片類比水波效果的重點在於類比水波的折射效果。出現水波的時候,相鄰兩個點之間的高度不一致,出現了一定的高度差,假定我們從正上方看這個水波,這個高度差就會產生一個折射效果,即我們看到的點應該在實際位置的偏下位置。一切從簡的話,這個位置位移多少就直接由這個高度差來決定算了。就簡單類比一下,其實運行之後的效果也不是那麼的差。

       一段段代碼的分析:

       buf1和buf2分別用來儲存一個像素點在一次渲染前和渲染後的振幅,BitMap1,BitMap2用來儲存獲得的圖片像素

1 short[] buf2;
2 short[] buf1;
3 int[] Bitmap2;
4 int[] Bitmap1;
5 buf2 = new
short[BACKWIDTH * BACKHEIGHT];
6 buf1 = new
short[BACKWIDTH * BACKHEIGHT];
7   
8 Bitmap2 = new
int[BACKWIDTH * BACKHEIGHT];
9 Bitmap1 = new
int[BACKWIDTH * BACKHEIGHT];

      扔石頭,如上所述
      則第一時間水面上被石頭打到的那部分水就會往下沉(振幅變為負)。

01 void
DropStone(
int x,// x座標
02 int
y,
// y座標
03 int
stonesize,
// 波源半徑
04 int
stoneweight)
// 波源能量
05 {
06 for
(
int posx = x - stonesize; posx < x + stonesize; posx++)
07 for
(
int posy = y - stonesize; posy < y + stonesize; posy++)
08 if
((posx - x) * (posx - x) + (posy - y) * (posy - y) < stonesize* stonesize)
09 buf1[BACKWIDTH * posy + posx] = (short) -stoneweight;
10 }

        這一段則是現實擴散的過程。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變成移位。得到新的振幅之後,執行衰減。

01 void
RippleSpread() {
02 for
(
int i = BACKWIDTH; i < BACKWIDTH * BACKHEIGHT - BACKWIDTH; i++) {
03 // 波能擴散
04 buf2[i] = (short) (((buf1[i -
1] + buf1[i +
1]+ buf1[i - BACKWIDTH] + buf1[i + BACKWIDTH]) >>
1) - buf2[i]);
05 // 波能衰減
06 buf2[i] -= buf2[i] >> 5;
07 }
08 // 交換波能資料緩衝區
09 short[] ptmp = buf1;
10 buf1 = buf2;
11 buf2 = ptmp;
12 }

      擴散一次之後,則根據相鄰像素點之間的高度差(即振幅差)計算出位移量Xoff和Yoff,上面說過的,位移就直接等於高度差算了,一切從簡。然後將位移加到具體的像素裡頭,新的像素為原來的像素加上位移之後的像素。

01 /* 渲染你水紋效果 */
02 void
render() {
03 int
xoff, yoff;
04 int
k = BACKWIDTH;
05 for
(
int i =
1; i < BACKHEIGHT -
1; i++) {
06 for
(
int j =
0; j < BACKWIDTH; j++) {
07 // 計算位移量
08 xoff = buf1[k - 1] - buf1[k +
1];
09 yoff = buf1[k - BACKWIDTH] - buf1[k + BACKWIDTH];
10 // 判斷座標是否在視窗範圍內
11 if
((i + yoff) <
0) {
12 k++;
13 continue;
14 }
15   
16   
17 if
((i + yoff) > BACKHEIGHT) {
18 k++;
19 continue;
20 }
21   
22 if
((j + xoff) <
0) {
23 k++;
24 continue;
25 }
26   
27 if
((j + xoff) > BACKWIDTH) {
28 k++;
29 continue;
30 }
31   
32 // 計算出位移象素和原始象素的記憶體位址位移量
33 int
pos1, pos2;
34 pos1 = BACKWIDTH * (i + yoff) + (j + xoff);
35 pos2 = BACKWIDTH * i + j;
36 Bitmap2[pos2++] = Bitmap1[pos1++];
37 k++;
38 }
39 }
40   
41 }

       最後,把這些加到線程裡頭,DropStone方法在onKeyUp事件中調用,線程繪圖了,每過50ms就擴撒一次,直至水面平靜。

01 public
void
run() {
02 while
(!Thread.currentThread().isInterrupted()) {
03 try
{
04 Thread.sleep(50);
05 } catch
(InterruptedException e) {
06 Thread.currentThread().interrupt();
07 }
08   
09 RippleSpread();
10 render();
11 // 使用postInvalidate可以直接線上程中更新介面
12 postInvalidate();
13 }
14   
15 }

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.