せっかくJavaを使うので動くものを作ってみました. 大変簡単な割に結構炎に見えるというのが大変嬉しいところです.
原理はというと火種をいくつか用意して,画面を更新する度に ぼかしながら画面上部へ向かって押し上げていくだけです. 例えば上のプログラムでは(x-1,y-1)の値を決めるときには(x-1, y),(x, y),(x+1, y), (x, y-1),(x, y+1)の平均をとっています(画面の左上が原点で右方向にX座標,下方向にY 座標となっている事に注意).それではソースコードを見てみます.
//カラーパレット作成 color = new Color[128]; for(int i=0, index=0; i<32; i++, index++) color[index] = new Color(i*8, 0, 0); for(int i=0, index=32; i<32; i++, index++) color[index] = new Color(255, i*8, 0); for(int i=0, index=64; i<64; i++, index++) color[index] = new Color(255, 255, i*4);
恐らくこの部分が一番重要です. この部分では使う色を事前に作成しています. もちろんRGBそれぞれの値を別々に上のアルゴリズムに基づいて計算しても いいのですが,こちらの方が簡潔でよりうまく炎に見えてくれると思います. この場合はcolorの配列のインデックスに対して上のアルゴリズムを適用します. もちろんindexの値が小さい程暗い色にする必要があります. ちなみに色の設定の出来によって結果の質が大きく左右されます.
//色をぼやかせて上に押し上げる for(int i=1; i< height-1; i++){ for(int j=1; j< width-1; j++){ pallet[(i-1)*width + j] = (pallet[i*width + j] + pallet[i*width + j-1] + pallet[i*width + j+1] + pallet[(i-1)*width + j] + pallet[(i+1)*width + j])/5; } }
この部分が前述のアルゴリズムの部分です. palletの配列はcolorの配列のインデックスを保持しています. ただし,これだけではうまく炎に見えません. というのも現実の炎にはゆらぎが存在するからです. そこで火種の部分を点滅させてみます. つまりランダムに火種を作ればいいのです. とは言え,画面のいたるところに火種が散らばってもうまくいきませんので, あらかじめ火種を作る場所を決めておきます.その上で乱数によって表示するかどうか 毎回判断することにします.
seedparam = new int[width * height]; //ランダムにパラメータを作成 for(int i=(height-2)*width; i<width*height; i++) { seedparam[i] = (int)(128*Math.random()); }
seedparamの配列のインデックスはRGB情報を保持させるimageの配列のインデックス に対応しています.つまり上のソースでは画像の最後の2行を火種を作る場所に 決めています.パラメータを128段階にしていますが,これは毎回画像を更新する際に ランダムに閾値を設け,その閾値よりも値が大きければ火種とします. 毎回閾値がランダムに変化するために結果的に火種がランダムに作られ,ゆらぎが発生します. ちなみにソース内に数値をべた書きしていて申し訳ないのですが, ここでの128は適当な値で,colorの配列数とは関係ありませんので注意してください.
//閾値をランダムに変化させ、火種をランダムに点火させる for(int i=0; i<width*height; i++){ if(seedparam[i]!=0&&seedparam[i]>(int)(firelevel*Math.random())){ pallet[i] = 127; }else if(seedparam[i]!=0){ pallet[i] = 0; } }
ここでseedparamがゼロで無い部分は火種となり得る場所ですので, ランダムに閾値を変化させ,閾値を越えればパレットの中で一番明るい色の インデックスである127を代入しておきます.越えなければ火種にしませんので ゼロにしておきます.
炎のアルゴリズムの説明は以上ですが,それ以外の部分を一部説明しておきます.
mims = new MemoryImageSource(width, height, image, 0, width); mims.setAnimated(true); buffer = createImage(mims);
imageはRGBが保持されているint型の一次元配列です. これを引数としてMemoryImageSourceのインスタンス生成をし, createImageに放りこむ事でImageのオブジェクトを返してくれます. またsetAnimatedにtrueを入れることでimageの配列を変更した後に
mims.newPixels();
とすればImageオブジェクトにimageが反映されます.
$ appletviewer FireEffect.java
でアプレットを起動できます.