火焰演算法(模糊演算法運用)

來源:互聯網
上載者:User

好久之前,看到了水傳輸速率效的演算法,最近又看到了火焰特效的演算法,兩者之間有很多的共同之處,我就將一篇關於火焰特效的文章翻譯了一下,一共大家參閱,如有不足之處,請見量!由於本文作者寫得很幽默,本人的水平有限,故有些地方還是採用的原文。  
   
  火焰特效  
  著   Shaun   Patterson   譯   beauli  
  火焰效果也許是我在所有特效中最喜歡的特效之一。當你設計出一個非常有效火焰常式,這是非常值得的。  
  在本文中,我將向你展示如何在DirectX   5.0下製作一個非常美麗的火焰效果。我想只要你認真的學習之後,你將能夠用任何語言來進行設計。  
  首先,在本文中我將不講述怎樣建立DirectDraw   Surface或者在代碼中添加任何有趣的實際代碼(譯註:即本文僅是火焰特效的演算法理論介紹)。我猜想你能在Sweet.Oblivion或GPMega上找到。然而,我將向你展示如何建立一個Palette,做一個簡單的迴圈,然後畫出火焰來。火焰實際上是一種比較簡單的特效,對它編程有一點娛樂的味道。  
  不幸的事,我不懂彙編,所以它並不是在整個世界上最快的火焰常式。但是它卻不可置信的在我的P2上快速的運行,而且在我的P75上表現得也不錯哦。  
   
  好了,下面轉入正題。首先你想讓你的代碼能真正的運行,你必須要建立一個Palette。我是這樣做的:  
  在底部依次分配為黑色(在火焰效果的頂部),紅色,橙色和黃色。  
  我僅僅用了一些迴圈就建立起了屬於我們自己的256種顏色。現在我們需要一個“容器”來盛放我們的調色盤資訊。  
  LPDIRECTDRAWPALETTE   lpDDPal;             //   the   palette   object  
  PALETTEENTRY           mypal[256];             //   stores   palette   stuff  
  我想這是非常恰當的注釋。現在,我們需要載入我們的資訊。首先我們載入我們的RGB資訊到每一個mypal的數組條目中。(為什麼我使用迴圈)  
  index   =   0;  
   
  for   (index=95;index<200;index++)  
  {  
          mypal[index].peRed       =   index+70;  
          mypal[index].peGreen   =   index+30;  
          mypal[index].peBlue     =   rand()%10;  
  }  
  for   (index   =   1;   index   <   35;   index++)  
  {  
          mypal[index].peRed       =   index+25;  
          mypal[index].peGreen   =   rand()%10;  
          mypal[index].peBlue     =   rand()%10;  
  }  
  for   (index   =   35;   index   <   55;   index++)  
  {  
          mypal[index].peRed       =   index+25;  
          mypal[index].peGreen   =   index-25;  
          mypal[index].peBlue     =   rand()%10;  
  }  
  for   (index   =   55;   index   <   95;   index++)  
  {  
          mypal[index].peRed   =   index+75;  
          mypal[index].peGreen   =   index;  
          mypal[index].peBlue   =   rand()%5;  
  }  
  for(index   =   200;   index   <   255;   index++)  
  {  
          mypal[index].peRed   =   index;  
          mypal[index].peGreen   =   index-rand()%25;  
          mypal[index].peBlue   =   rand()%5;  
  }  
  這是我發現的最好的結構。雖然我花了不到10分鐘時間。  
  所有的一切基本上是為了將RGB值裝載入屬於我們的每一個調色盤數組的條目中。mypal結構中的peRed成員是紅色的值,peGreen是綠色的值,以此類推。  
  現在我們已經接近調色盤對象(lpDDPal)和首頁面(primary   surface)的值。我們只需要使用下面的語句就可以使用調色盤。  
  lpDD->CreatePalette(DDPCAPS_8BIT   |   DDPCAPS_ALLOW256,   mypal,   &lpDDPal,   NULL);  
  lpDDSPrimary->SetPalette(lpDDPal);  
  lpDD——DirectDraw對象  
  lpDDSPrimary——Primary   Surface  
   
  With   me   so   far?   No?   Hmm,   not   good.   Go   read   another   tutorial   then!   Geez!   People   these   days...    
  Still   holding   your   breath?   Well   breathe.   The   rest   is   a   breeze.  
   
   
  dun   dun   dun   dunnnn  
  火焰演算法  
  首先,為了能夠使我們能進行繪畫,我製作了一個小程式來鎖住背景頁面。  
   
  unsigned   char   *Lock_Back_Buffer   (void)  
  {  
  DDSURFACEDESC   ddsd;  
  HRESULT   ret;  
  ddsd.dwSize   =   sizeof(ddsd);  
   
  ret   =   DDERR_WASSTILLDRAWING;  
   
  while   (ret   ==   DDERR_WASSTILLDRAWING)  
          ret   =   lpDDSBack->Lock(NULL,   &ddsd,   0,   NULL);  
   
  return   (ret   ==   DD_OK   ?   (unsigned   char   *)ddsd.lpSurface   :   NULL);  
  }  
   
  這個函數返回一個指向螢幕結構的unsigned   char指標,因此我們需要建立一個螢幕結構。  
   
  unsigned   char   *double_buffer   =   NULL;  
   
  接下來,我們給它分配一些記憶體空間  
   
  double_buffer   =   (unsigned   char   *)malloc(307200);  
   
  Oh   yeah。我是工作在640x480x8bit   的分辨力下的。  
   
  接下來,我們需要另一個unsigned   char指標數組來儲存我們的火焰像素。  
   
  unsigned   char   *fire_buffer   =   NULL;  
  fire_buffer   =   (unsigned   char   *)malloc(307200);  
   
  現在可不要忘了在結束時顯式的釋放這些空間。  
   
  free(double_buffer);   free(fire_buffer);  
   
  Ok..whew.現在開始製作火焰部分,在螢幕的底部(最後一行)繪一些隨機點,這些隨即點的顏色可以任意給定為0或者255。如下所作:  
   
  for(x   =   1;   x   <   637;   x+=rand()%3)  
  {  
          if(rand()%2)  
                  fire_buffer[(480*640)   +   x]   =   255;  
          else  
                  fire_buffer[(480*640)   +   x]   =   0;    
  }  
  以上是我所喜歡的做法,當然你也可以不使用隨機點。  
  好了,這就是我製作火焰的方法,選擇一個像素的背景像素然後用像素的數量去除。現在你也許在想——WHAT?!好了,很簡單。你是不是有一個點?如果你熟悉像素的繪製,你一定也知道在1d數組中尋找一個指標數組中的點的方法——Y   position   *   ScreenWidth   +   X   position。還糊塗嗎?Good。這表示你已經開始思考了=)  
  就像打字機一樣的思考它吧。  
   
  tap   tap   tap   tap   tap   tap   CA   CHING   -   next   line  
  tap   tap   tap   tap   tap   tap   CA   CHING   -   next   line  
  tap   tap   tap   tap   tap   tap   CA   CHING   -   next   line  
   
  etc...  
   
  在這個例子中螢幕只有6個tap寬&#61514;。好了,說你想要畫的像素在第二行第三列。2*6=?12。  
  12+3=?15。whoa!一口氣算完。  
  現在開始計數1,2,3。。。15。等一下,我們現在是在第三行!!開始計數是從0開始然後1,   2   ,3   的對嗎?所以2*6=12,12+3=15(譯註:原文為so   1*6   is   6.   Then   6   +   3   is   9.恐為筆誤)。現在開始轉入正題。  
  哎!接下來該怎麼做呢?對了,使用迴圈。需要通過迴圈我們要繪製火焰的整個螢幕。開始進入迴圈,首先是Y然後X。你會馬上就能明白的。在X的迴圈中,就像我以前所說的,平均周圍的像素來獲得新像素。不要忘記調色盤(Palette)。假如我們的像素點隨機值為255,就是黃色。如果是0,就是黑色。接下來,255+0=?255。255/2=?128。whoa!!我們在調色盤中獲得了桔紅色地區的點。這是所有火焰的最基本的點。在火焰上升時,對顏色進行平均你能得到一個又一個新的顏色。這時候如果你思考得越多,你就會變得越明白。開始黃色,變成橙色,紅色,最後當它達到一個非常紅的時候變成黑色消失。當火焰上升時,它將會變成很cool的形狀。你將能夠在例子中看到。  
   
  偽碼:  
   
  for   y   =   1,   y   to   screenheight,   increment   y  
          for   x   =   1,   x   to   screenwidth,   increment   x  
                  find   our   offset   -   what   pixel   we   are   going   to   start    
                            averaging   around   -   (Y*ScreenHeight)+x  
                  add   up   the   surrounding   pixels   -   all   eight   of   them  
                  divide   that   total   by   8   -   hard   concept   there   =)  
                   
                  now   if   that   value   is   not   0   -   not   black  
                  we   decrement   it   -   subtract   1   =)  
   
          end  
   
  Draw   to   our   back   buffer  
   
  end  
   
  好了,接下來是C++代碼  
   
  int   x,y,fireoffset;  
   
   
  //calculate   the   bottom   line   pixels  
  for(x   =   1;   x   <   637;   x+=rand()%3)  
  {  
          if(rand()%2)  
                  fire_buffer[(480*640)   +   X]   =   255;  
          else  
                  fire_buffer[(480*640)   +   X]   =   0;    
          }  
  }  
   
  //CALCULATE   THE   SURROUNDING   PIXELS  
        for(y   =   1;   y   <   480;   ++y)  
        {  
              for(x   =   1;   x   <640;   ++x)  
              {  
                    fireoffset   =   (y*640)   +   x;  
                    firevalue   =   ((fire_buffer[fireoffset-640]   +  
                          fire_buffer[fireoffset+640]   +  
                          fire_buffer[fireoffset+1]   +  
                          fire_buffer[fireoffset-1]   +  
                          fire_buffer[fireoffset-641]   +  
                          fire_buffer[fireoffset-639]   +  
                          fire_buffer[fireoffset+641]   +  
                          fire_buffer[fireoffset+639])   /   8);   //   this   can   be   optimized   by   a    
                                            //   look   up   table   as   I'll   show   you   later  
                                                   
                    if(firevalue   !=   0)   //   is   it   black?  
                    {  
                          --firevalue;               //   Nope.   Beam   me   down   Scotty.  
                          fire_buffer[fireoffset-640]   =   firevalue;         //   Plot   that   new   color  
                                                                          //   on   the   above   pixel  
                                                                          //   remember   the   typewriter   analogy  
                    }  
              }  
        }  
   
        double_buffer   =   Lock_Back_Buffer();       //   Remember   this   function?     Good  
        memcpy(double_buffer,   fire_buffer,   (640*480));     //   Copy   fire   buffer   to   the   screen  
        lpddsback->Unlock(NULL);                           //   Unlock!   Important!   you   have   no   idear!  
   
  好了,也許已開始你可能認為很不好理解,但是,實際上只要你好好地想一下,這是非常簡單的。  
  我們可以發現這個像素點,上面我們討論過的——Y*640+X,這就是我們的fireoffset——這個點是我們用來平均的中心點。  
  現在fireoffset-640=?位於這個點正上方的點是嗎?fireoffset-639=?位於這個點上方但是稍微偏右的點。所以是右上方的點。Fireoffset+640=?正下方的點。明白了嗎??  
  我說過它是很簡單的不是嗎?Ha!是你自己你不相信我。我想也許已開始可能有點迷糊。但是你好好地想一下之後,你就會一下子明白過來——像我一樣。(譯註:為了協助理解,可以參考)  

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.