モノ創りで国造りを

ハード/ソフト問わず知見をまとめてます

Processingで波動のシミュレーション

Processingで波動のシミュレーションをやってみものの、、、
遅すぎ!  

f:id:yuji2yuji:20180831062204g:plain

 

グリッドを100x100くらいにすればまぁなんとかなりますが、
200x200(上のgif)でおっそいなーと感じ、
600x600だと耐えられないレベル

私のPC、16ギガなんですけど!
シミュレーションといっても、難しい計算してないんですけど!
フレームレート上げても、変わらないんですけど!

これは実用に耐えないと思ったら、   GPU使って高速化できるとの記事があったので、早速試してみた   が、遅い。  

  Processingはシミュレーションが苦手いうわけではなく、
画像の描画のオーバーヘッドが重く、その結果遅くなっているようです。   例えば600x600の合計36万個のデータの演算そのものには、   さほど時間かかりませんでした。  

例えば、FPSを60にした場合、draw関数は10秒でほぼ600回実行されました。

 

が、この演算したデータをpoint関数でキャンバス上に描画する際に、
オーバーヘッドが重くのしかかります。
600x600の場合では、FPSが60でも、10秒で20回しかdraw関数は実行されませんでした。
FPS=2ですよ、2。
 

なにか高速化する手段はないかと探しましたが、
ここはPixelsの出番です。

 

Pixelsは名前の通り、画素を取り扱うものです。 
簡単に述べると、

loadPixels();//キャンバス全体の各画素データを保存
pixels[y*width+x];//キャンバスのx,y座標の画素データを設定
updatePixel();//pixelデータをキャンバスに描画

といった具合です。

 

シミュレーション画像を描画する際は、
pixelsを使用するとオーバーヘッドが抑えられて高速に描画できます。
試しにpixelを使用して600x600のグリッドでシミュレーションをしてみると
f:id:yuji2yuji:20180831073534g:plain
早い!!
これはお薦め!
ソースコードも、わずか3行加えるだけで済みました。
pixelに関連するのは以下のソースのほんのわずか3行です。

loadPixels();//pixelを使用するために必要。

pixels[Y*width + X] = color(amplitude[X][Y]);//各データをpixelデータにする

updatePixels();//pixelデータで画像を表示

600x600でこのスピード感。ありがとうpixels。

ソースは以下

void setup() {
  size(600, 600);

  noStroke();
  background(0, 0, midValue);
  frameRate(60);

  initialiseConditions();
  setBoundaryConditions();

  nextAmplitude = new float[numX][numY];
  loadPixels();//pixelを使用するために必要。
}

void draw() {
  //  shader(sd);
  noStroke();
  background(0, 0, midValue);
  for (int X=1; X<numX-1; X++) {
    for (int Y=1; Y<numY-1; Y++) {
      nextAmplitude[X][Y] = 2*amplitude[X][Y];
      nextAmplitude[X][Y] -= preAmplitude[X][Y];
      nextAmplitude[X][Y] += constValue*(amplitude[X-deltaX][Y]
        +amplitude[X+deltaX][Y]
        +amplitude[X][Y-deltaX]
        +amplitude[X][Y+deltaX]
        -4*amplitude[X][Y]);
    }
  }

  for (int X=0; X<numX; X++) {
    for (int Y=0; Y<numY; Y++) {
      preAmplitude[X][Y] = amplitude[X][Y];
      pixels[Y*width + X] = color(amplitude[X][Y]);//各データをpixelデータにする
      amplitude[X][Y] = nextAmplitude[X][Y];
    }
  }
  updatePixels();//pixelで表示
  //Boundary Conditions
  setBoundaryConditions();
}


//境界条件決める関数
void setBoundaryConditions() {
  //Boundary Conditions
  for (int X=0; X<numX; X++) {
    for (int Y=0; Y<numY; Y++) {
      amplitude[0][Y] = fixedBound;
      amplitude[numX-1][Y] = fixedBound;
    }
    amplitude[X][0] =fixedBound;
    amplitude[X][numY-1] = fixedBound;
  }
}


//各種パラメーター
int cnt = 0;
int deltaX = 1;
int deltaTime = 1;
float speed = 0.3;
float constValue = pow(float(deltaTime), 2)*pow(speed, 2)/pow(float(deltaX), 2);

float[][] amplitude;
float[][] preAmplitude;
float[][] nextAmplitude;

int numX = 600;
int numY = 600;

float midValue = 128;

float fixedBound = 128;
float zeroBound = 128;

void initialiseConditions() {
  //Clear amplitude and preAmplitude.
  amplitude =new float[numX][numY];
  preAmplitude =new float[numX][numY];
  for (int X=0; X<numX; X++) {
    for (int Y=0; Y<numY; Y++) {
      amplitude[X][Y] = midValue;
      preAmplitude[X][Y] = midValue;
    }
  }

  //Input Initial value.
  preAmplitude[300][250] = 20;

  preAmplitude[300][350] = 20;
}