Efekt ognia

Efekt ognia pamiętam "od zawsze", nie może go zabraknąć i w tym opracowaniu.
Algorytm ogólny jest następujący:
  1. Znależć gradient kolorów dla ognia
  2. Narysować podstawę "ognia" tzn dolny (najniższy) wiersz apletu - ogień pali się od dołu
  3. Przenieść ogień wyżej : pobrać kolor z niższego rzędu, "osłabić" i zapalić losowo na lewo lub prawo, w niewielkiej odległości (najlepiej losując - zapewni to naturalność płomienia)
  4. Krok (3) powtarzać dowolną ilość razy..
To oczywiście podstawowy algorytm, w wielu rozwiązaniach autorzy, przed pokazanie rezultatu dodatkowo wygładzają płomień przez uśrednianie koloru piksla z kolorów dowolnej kombinacji jego sąsiadów.

Teraz szczegóły programistyczne - a te będą zależały od wyboru języka.
Jak uzyskać dostęp do koloru piksla i jak ten piksel narysować ?
Zdecydujemy się na Javę. Tu piksel rysujemy : g.drawLine(x,y,x,y) lub g.fillRect(x,y,1,1) !
Java pod tym względem ma swoją specyfikę : informacja o kolorze piksla i jego "naturalne" zapalanie możliwe jest tylko jeśli rysujemy na mapie bitowej, najlepiej typu BufferedImage.
Po drugie będzie nam potrzebny właściwie numer koloru w wybranym przez nas gradiencie. Te przesłanki zdecydują o wyborze zastosowanej strukturze danych.

W zadaniu zasosujemy następującą strukturę:

int scr[][]=new int[200][300]; 
int pal[]=new int[256]; 
BufferedImage mapa;
tablica 200x300 (wymiary apletu) do pamiętania numerów koloru ognia
tablica z gradientem kolorów ognia
mapa bitowa wielkości apletu, aby rysować ogień


Przygotuję w tablicy pal gradient kolorów ognia, tablicę scr - podręczną pamięć kolorów (ich numeryr) oraz nauczę się rysować "piksel ognia" (notując jednocześnie kolor)
void paleta() 
{ 
  for(int i=0;i<200;i++) 
     for(int j=0;j<300;j++) scr[i][j]=0; 

  for(int i=1;i<128;i++) pal[i]=(2*i<<16)|(i<<8)|(2); 
  for(int i=0;i<64;i++) pal[i+128]=(254<<16)|(i+127<<8)|(i+2); 
  for(int i=0;i<66;i++) pal[i+190]=(254)|(i+190<<8)|(i+65); 
  pal[0]=0;pal[1]=0; 
  pal[255]=(255<<16)|(255<<8)|(132); 
}
void piksel(int x,int y,int kolor) 
{ 
  if(x>0 && x<300 && y>0 && y<200) 
  { 
    mapa.setRGB(x,y,pal[kolor]); 
    scr[y][x]=kolor; 
  } 
}



Załóżmy, że nie decydujemy się na wygładzanie ognia - wtedy powinniśmy "palić ogień" od góry co jest oczywiście sprzeczne z fizyką, ale daje "od razu" niezły rezultat.
"Klatka" animacyjna ognia, rysowanego "od góry" może wyglądać tak:
void fire_g() 
{int tmp; 
  for (int x=1; x<300; x++) piksel(x,199,wys); /**/ 
  for (int x=1; x<300; x++) /***/ 
  { 
   for (int y=1; y<199; y++) 
    if(scr[y][x]<wys) 
     { 
      tmp = scr[y+1][x]; 
      if(tmp > 30) 
       piksel(x+(mat.losowa_c(4)-1), y ,tmp-(mat.losowa_c(4))); 
        else piksel(x, y, 0); 
     } 
  } 
}

gdzie w pętli /**/ zapalam ogień
w najniższym wierszu,

a pętli /***/ uzyskuję efekt
stopniowego przenoszenia "do góry", mimo odwrotnego rysowania !

Wygładzanie ognia to już szczegół, do indywidualnego roztrzygnięcia..

Mój aplet pokazuje dwie metody przenoszenia ognia :
V - "z góry" i ^ - "z dołu" .
Przy wyborze " z dołu" trzeba rzeczywiście zastosować wygładzanie typu np blur.

Jak wspomniałem już rysowanie "z góry" praktycznie od razu daje dobry rezultat, wygładzanie nawet chyba pogarsza rezultat. Ten przykład pokazuje, że warto eksperymentować..

Zobacz jeszcze na koniec wersję: