青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

羅朝輝(飄飄白云)

關注嵌入式操作系統(tǒng),移動平臺,圖形開發(fā)。-->加微博 ^_^

  C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
  85 隨筆 :: 0 文章 :: 169 評論 :: 0 Trackbacks

Android 上實現(xiàn)水波特效    

羅朝輝(http://m.shnenglu.com/kesalin

轉載請注明出處

說明:

       本文算法部分整理自 GameRes 上的資料,原作者 Imagic。我只是在學習 Android 的過程中,想到這個特效,然后就在Android 上實現(xiàn)出來,并在源算法的基礎上添加了雨滴滴落特效,以及劃過水面時的漣漪特效。 該程序在模擬器和真機上運行速度都較慢,需要進一步優(yōu)化或使用 JNI 實現(xiàn),如果你想到好的優(yōu)化算法,請聯(lián)系我:kesalin@gmail.com。

 

    示例程序下載:http://m.shnenglu.com/Files/kesalin/RippleDemo.zip


基礎知識:

在講解代碼之前,我們來回顧一下在高中的物理課上我們所學的關于水波的知識。水波有擴散,衰減,折射,反射,衍射等幾個特性:

 

擴散:當你投一塊石頭到水中,你會看到一個以石頭入水點為圓心所形成的一圈圈的水波,這里,你可能會被這個現(xiàn)象所誤導,以為水波上的每一點都是以石頭入水點為中心向外擴散的,這是錯誤的。實際上,水波上的任何一點在任何時候都是以自己為圓心向四周擴散的,之所以會形成一個環(huán)狀的水波,是因為水波的內部因為擴散的對稱而相互抵消了。

衰減:因為水是有阻尼的,否則,當你在水池中投入石頭,水波就會永不停止的震蕩下去。

折射:因為水波上不同地點的傾斜角度不同,所以我們從觀察點垂直往下看到的水底并不是在觀察點的正下方,而有一定的偏移。如果不考慮水面上部的光線反射,這就是我們能感覺到水波形狀的原因。

反射:水波遇到障礙物會反射。

衍射:在水池中央放上一塊礁石,或放一個中間有縫的隔板,那么就能看到水波的衍射現(xiàn)象了。

 

算法推導:

好了,有了這幾個特性,再運用數(shù)學和幾何知識,我們就可以模擬出真實的水波了。但是,如果你曾用3DMax做過水波的動畫,你就會知道要渲染出一幅真實形狀的水波畫面少說也得好幾十秒,而我們現(xiàn)在需要的是實時的渲染,每秒種至少也得渲染20幀才能使得水波得以平滑的顯示。考慮到電腦運算的速度,我們不可能按照正弦函數(shù)或精確的公式來構造水波,不能用乘除法,更不能用sin、cos等三角函數(shù),只能用一種取近似值的快速算法,盡管這種算法存在一定誤差,但是為了滿足實時動畫的要求,我們不得不這樣做。

 

首先我們要建立兩個與水池圖象一樣大小的數(shù)組buf1[PoolWidth * PoolHeight]buf2[PoolWidth * PoolHeight]PoolWidth 為水池圖象的象素寬度、PoolHeight 為水池圖象的象素高度),用來保存水面上每一個點的前一時刻和后一時刻波幅數(shù)據(jù),因為波幅也就代表了波的能量,所以在后面我們稱這兩個數(shù)組為波能緩沖區(qū)。水面在初始狀態(tài)時是一個平面,各點的波幅都為0,所以,這兩個數(shù)組的初始值都等于0

下面來推導計算波幅的公式

我們假設存在這樣一個一次公式,可以在任意時刻根據(jù)某一個點周圍前、后、左、右四個點以及該點自身的振幅來推算出下一時刻該點的振幅,那么,我們就有可能用歸納法求出任意時刻這個水面上任意一點的振幅。如左圖,你可以看到,某一時刻,X0點的振幅除了受X0點自身振幅的影響外,同時受來自它周圍前、后、左、右四個點(X1、X2、X3X4)的影響(為了簡化,我們忽略了其它所有點),而且,這四個點對X0點的影響力可以說是機會均等的。那么我們可以假設這個一次公式為:

 

X0’ = a * (X1 + X2 + X3 + X4) + b * X0            (公式1)

 

a, b為待定系數(shù),X0’ X0點下一時刻的振幅,

X0X1、X2、X3、X4為當前時刻的振幅

 

下面我們來求解ab。

假設水的阻尼為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、再減去該點的波幅。

 

應該注意到,水在實際中是存在阻尼的,否則,用上面這個公式,一旦你在水中增加一個波源,水面將永不停止的震蕩下去。所以,還需要對波幅數(shù)據(jù)進行衰減處理,讓每一個點在經(jīng)過一次計算后,波幅都比理想值按一定的比例降低。這個衰減率經(jīng)過測試,用1/32比較合適,也就是1/2^5??梢酝ㄟ^移位運算很快的獲得。

到這里,水波特效算法中最艱難的部分已經(jīng)明了,下面是Android 源程序中計算波幅數(shù)據(jù)的代碼。

 

// 某點下一時刻的波幅算法為:上下左右四點的波幅和的一半減去當前波幅,即

//    X0' =X1 + X2 + X3 + X4/ 2 - X0

//  +----x3----+

//  +     |      +

//  +     |      +

// x1---x0----x2

//  +     |      +

//  +     |      +

//  +----x4----+

//

void rippleSpread()

{

    int pixels = m_width * (m_height - 1);

    for (int i = m_width; i < pixels; ++i) {

       // 波能擴散:上下左右四點的波幅和的一半減去當前波幅

       // X0' =X1 + X2 + X3 + X4/ 2 - X0

       //

       m_buf2[i] =

(short)(((m_buf1[i - 1] + m_buf1[i + 1]+

          m_buf1[i - m_width] + m_buf1[i + m_width]) >> 1)

- m_buf2[i]);

 

       // 波能衰減 1/32

      //

       m_buf2[i] -= m_buf2[i] >> 5;

    }

 

    //交換波能數(shù)據(jù)緩沖區(qū)

    short[] temp = m_buf1;

    m_buf1 = m_buf2;

    m_buf2 = temp;

}


渲染:

然后我們可以根據(jù)算出的波幅數(shù)據(jù)對頁面進行渲染。

 

因為水的折射,當水面不與我們的視線相垂直的時候,我們所看到的水下的景物并不是在觀察點的正下方,而存在一定的偏移。偏移的程度與水波的斜率,水的折射率和水的深度都有關系,如果要進行精確的計算的話,顯然是很不現(xiàn)實的。同樣,我們只需要做線性的近似處理就行了。因為水面越傾斜,所看到的水下景物偏移量就越大,所以,我們可以近似的用水面上某點的前后、左右兩點的波幅之差來代表所看到水底景物的偏移量。

 

在程序中,用一個頁面裝載原始的圖像,用另外一個頁面來進行渲染。先取得指向兩個頁面內存區(qū)的指針 src dst,然后用根據(jù)偏移量將原始圖像上的每一個象素復制到渲染頁面上。進行頁面渲染的代碼如下:

 

void rippleRender()

{

    int offset;

    int i = m_width;

    int length = m_width * m_height;

    for (int y = 1; y < m_height - 1; ++y) {

       for (int x = 0; x < m_width; ++x, ++i) {

          // 計算出偏移象素和原始象素的內存地址偏移量 :

          //offset = width * yoffset + xoffset

          offset = (m_width * (m_buf1[i - m_width] - m_buf1[i + m_width])) + (m_buf1[i - 1] - m_buf1[i + 1]);

            

          // 判斷坐標是否在范圍內

          if (i + offset > 0 && i + offset < length) {

             m_bitmap2[i] = m_bitmap1[i + offset];

          }

          else {

             m_bitmap2[i] = m_bitmap1[i];

          }

       }

    }

}

 

增加波源:

俗話說:無風不起浪,為了形成水波,我們必須在水池中加入波源,你可以想象成向水中投入石頭,形成的波源的大小和能量與石頭的半徑和你扔石頭的力量都有關系。知道了這些,那么好,我們只要修改波能數(shù)據(jù)緩沖區(qū)buf,讓它在石頭入水的地點來一個負的尖脈沖,即讓buf[x,y] = -n。經(jīng)過實驗,n的范圍在(32 ~ 128)之間比較合適。

 

控制波源半徑也好辦,你只要以石頭入水中心點為圓心,畫一個以石頭半徑為半徑的圓,讓這個圓中所有的點都來這么一個負的尖脈沖就可以了(這里也做了近似處理)。

增加波源的代碼如下:

 

// stoneSize    : 波源半徑

// stoneWeight : 波源能量

//

void dropStone(int x, int y, int stoneSize, int stoneWeight)

{

    // 判斷坐標是否在范圍內

    if ((x + stoneSize) > m_width || (y + stoneSize) > m_height

          || (x - stoneSize) < 0 || (y - stoneSize) < 0) {

       return;

    }

 

    int value = stoneSize * stoneSize;

    short weight = (short)-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)

            < value)

         {

                m_buf1[m_width * posy + posx] = weight;

         }

      }

   }

}

 

如果我們想要模擬在水面劃過時引起的漣漪效果,那么我們還需要增加新的算法函數(shù) breasenhamDrop

 

void dropStoneLine(int x, int y, int stoneSize, int stoneWeight) {

   // 判斷坐標是否在屏幕范圍內

   if ((x + stoneSize) > m_width || (y + stoneSize) > m_height

      || (x - stoneSize) < 0 || (y - stoneSize) < 0) {

         return;

   }

 

   for (int posx = x - stoneSize; posx < x + stoneSize; ++posx)    {

      for (int posy = y - stoneSize; posy < y + stoneSize; ++posy)       {

         m_buf1[m_width * posy + posx] = -40;

      }

   }

}

 

// xs, ys : 起始點,xe, ye : 終止點

// size : 波源半徑,weight : 波源能量

void breasenhamDrop (int xs, int ys, int xe, int ye, int size, int weight)

{

   int dx = xe - xs;

   int dy = ye - ys;

   dx = (dx >= 0) ? dx : -dx;

   dy = (dy >= 0) ? dy : -dy;

 

   if (dx == 0 && dy == 0) {

      dropStoneLine(xs, ys, size, weight);

   }

   else if (dx == 0) {

       int yinc = (ye - ys != 0) ? 1 : -1;

       for(int i = 0; i < dy; ++i){

           dropStoneLine(xs, ys, size, weight);

           ys += yinc;

       }

   }

   else if (dy == 0) {

      int xinc = (xe - xs != 0) ? 1 : -1;

      for(int i = 0; i < dx; ++i){

         dropStoneLine(xs, ys, size, weight);

         xs += xinc;

      }

   }

   else if (dx > dy) {

      int p = (dy << 1) - dx;

      int inc1 = (dy << 1);

      int inc2 = ((dy - dx) << 1);

      int xinc = (xe - xs != 0) ? 1 : -1;

      int yinc = (ye - ys != 0) ? 1 : -1;

 

      for(int i = 0; i < dx; ++i) {

         dropStoneLine(xs, ys, size, weight);

         xs += xinc;

         if (p < 0) {

            p += inc1;

         }

         else {

            ys += yinc;

            p += inc2;

         }

      }

   }

   else {

      int p = (dx << 1) - dy;

      int inc1 = (dx << 1);

      int inc2 = ((dx - dy) << 1);

      int xinc = (xe - xs != 0) ? 1 : -1;

      int yinc = (ye - ys != 0) ? 1 : -1;

 

      for(int i = 0; i < dy; ++i) {

         dropStoneLine(xs, ys, size, weight);

         ys += yinc;

         if (p < 0) {

            p += inc1;

         }

         else {

            xs += xinc;

            p += inc2;

         }

      }

   }

}

 

 

效果圖:


劃過水面時的漣漪特效


雨滴滴落水面特效

 

結語:

這種用數(shù)據(jù)緩沖區(qū)對圖像進行水波處理的方法,有個最大的好處就是,程序運算和顯示的速度與水波的復雜程度是沒有關系的,無論水面是風平浪靜還是波濤洶涌,程序的fps始終保持不變,這一點你研究一下程序就應該可以看出來。


posted on 2010-09-01 13:19 羅朝輝 閱讀(3699) 評論(0)  編輯 收藏 引用 所屬分類: 計算機圖形學移動開發(fā)Algorithms
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美亚洲尤物久久| 亚洲福利久久| 国产精品丝袜91| 亚洲欧洲精品一区二区| 久久久国产精品一区二区三区| 亚洲欧洲精品一区二区三区| 午夜视频精品| 国精品一区二区三区| 欧美资源在线| 久久偷看各类wc女厕嘘嘘偷窃| 国产欧美不卡| 欧美成人中文字幕| 性色av一区二区怡红| 欧美大片在线看| 99re热这里只有精品视频| 亚洲精品在线观看免费| 欧美日韩精品是欧美日韩精品| 亚洲免费成人av电影| 亚洲国产高清自拍| 欧美三级中文字幕在线观看| 久久综合伊人77777麻豆| 亚洲成色999久久网站| 欧美成人一二三| 午夜视频在线观看一区| 久久激情综合网| 亚洲手机成人高清视频| 狠狠88综合久久久久综合网| 亚洲精品国产精品国产自| 国产区精品视频| 亚洲精品在线视频| 激情欧美亚洲| 国产亚洲一区二区三区| 欧美国产日韩视频| 国产视频在线一区二区 | 亚洲午夜精品一区二区三区他趣| 亚洲日本中文字幕区| 国产日韩欧美在线看| 亚洲精品一区二区三区不| 国产日韩视频| 亚洲一区免费视频| 99re热这里只有精品免费视频| 久久蜜桃资源一区二区老牛 | 在线免费观看视频一区| 国产精品99久久久久久有的能看| 亚洲人成网站色ww在线| 久久久久国产一区二区三区| 久久久久国产精品www| 国产欧美在线| 久久福利视频导航| 免费日韩av片| 亚洲日本在线视频观看| 国产欧美日韩中文字幕在线| 老司机成人网| 女生裸体视频一区二区三区| 久久综合伊人77777麻豆| 韩国一区电影| 欧美激情精品久久久久久大尺度| 亚洲国产精品久久精品怡红院| 激情婷婷久久| 欧美激情无毛| 午夜精品久久久久久久99热浪潮| 亚洲日本欧美日韩高观看| 欧美激情第三页| 久久噜噜亚洲综合| 亚洲自拍偷拍福利| 久久九九精品| 亚洲精品国产视频| 国产精品激情偷乱一区二区∴| 亚洲欧美另类在线| 亚洲欧洲一二三| 久久精品一本| 亚洲一区视频在线| 亚洲激情视频网| 国产日韩欧美日韩| 久久精品国产一区二区三区| 亚洲黄页一区| 1024国产精品| 一区二区三区在线免费播放| 国产精品午夜国产小视频| 欧美激情麻豆| 六十路精品视频| 美女精品自拍一二三四| 久久久中精品2020中文| 亚洲欧美国产不卡| 一本色道久久综合精品竹菊| 亚洲国产日韩一级| 亚洲级视频在线观看免费1级| 久久久7777| 欧美不卡一区| 亚洲大片在线观看| 亚洲国产欧美国产综合一区| 亚洲国产成人av| 亚洲精品久久久久久久久久久久久 | 亚洲综合色在线| 99ri日韩精品视频| 亚洲电影免费在线观看| 亚洲区中文字幕| 在线亚洲高清视频| 午夜精品理论片| 久久久久国产精品厨房| 欧美xxxx在线观看| 欧美成人资源网| 亚洲精品1234| 在线观看欧美亚洲| 葵司免费一区二区三区四区五区| 性色av香蕉一区二区| 久久久久久久久久看片| 麻豆精品精华液| 欧美日一区二区在线观看| 国产精品久久久久永久免费观看| 国产日韩精品一区二区| 国语自产精品视频在线看一大j8| 在线精品亚洲一区二区| 日韩视频在线一区二区三区| 久久爱www久久做| 亚洲国产综合在线看不卡| 中文国产成人精品久久一| 久久精品在线免费观看| 欧美黄色aaaa| 国产亚洲一区二区精品| 亚洲午夜精品网| 亚洲激情成人| 午夜精品av| 国产嫩草一区二区三区在线观看 | 亚洲图片在线观看| 久久久久综合网| 亚洲欧美国产va在线影院| 欧美大秀在线观看| 狠狠久久婷婷| 久久久精品一品道一区| 亚洲欧美不卡| 国产亚洲精品成人av久久ww| 午夜精品婷婷| 午夜在线精品偷拍| 国产精品综合| 欧美在线黄色| 欧美在线观看视频一区二区| 国产色综合网| 国产精品国产三级国产aⅴ无密码| 狠狠爱成人网| 欧美大片免费观看| 欧美第一黄色网| avtt综合网| 中文网丁香综合网| 国产网站欧美日韩免费精品在线观看| 亚洲欧美精品一区| 亚洲免费小视频| 久久久久久9| 欧美在线一二三| 亚洲经典在线看| 亚洲在线免费观看| 在线免费观看日本一区| 日韩亚洲精品视频| 国内精品久久久久久| 亚洲精品国产系列| 国产一区二区三区电影在线观看 | 亚洲欧美日韩一区二区| 香蕉尹人综合在线观看| 亚洲久色影视| 新67194成人永久网站| 日韩午夜电影av| 久久免费少妇高潮久久精品99| 中文在线一区| 久久亚洲不卡| 久久米奇亚洲| 国产精品人人做人人爽人人添| 亚洲电影毛片| 亚洲国内欧美| 久久精品在线观看| 久久久精彩视频| 国产欧美在线| 香蕉免费一区二区三区在线观看 | 欧美日韩色婷婷| 欧美成人激情视频| 黄色成人av| 久久夜色精品国产欧美乱| 久久久高清一区二区三区| 国内精品模特av私拍在线观看| 91久久国产精品91久久性色| 亚洲欧美国产77777| 欧美日韩国产小视频在线观看| 欧美风情在线观看| 亚洲国产欧美在线人成| 欧美黄色一区二区| 91久久线看在观草草青青| 亚洲精品老司机| 欧美日韩伦理在线免费| 亚洲已满18点击进入久久| 久久成人亚洲| 亚洲国产精品视频一区| 欧美激情无毛| 午夜日韩在线观看| 91久久精品久久国产性色也91 | 欧美专区在线观看| 裸体歌舞表演一区二区| 亚洲精品一区中文| 国产精品免费福利| 免费久久99精品国产自在现线| 亚洲午夜在线| 亚洲大胆人体视频|