2012年12月21日金曜日

Curl Noise追記

GPGPU Advent Calender@21日目ですが、完全にネタ切れのため前回書き忘れていた部分を補足してお茶を濁します!
by jirohcl

まず、前回説明し忘れた重要なnoise関数。
float noise(float2 p,__global float2* rt,__global unsigned char* rti)
{
 float2 floor_p = floor(p);//float2(floor(p.x),floor(p.y))
 int i=(int)floor_p.x, j=(int)floor_p.y;
 float2 n00=rt[hash_idx(i,j,rti)];
 float2 n10=rt[hash_idx(i+1,j,rti)];
 float2 n01=rt[hash_idx(i,j+1,rti)];
 float2 n11=rt[hash_idx(i+1,j+1,rti)];

 float2 pf = p - floor_p;
 float sx= pow(pf.x,3)*(10-pf.x*(15-pf.x*6));
      float sy= pow(pf.y,3)*(10-pf.y*(15-pf.y*6));

 return bilerp(   dot(pf,n00)                   , dot(pf-(float2)(1.f,0.f),n10),
                         dot(pf-(float2)(0.f,1.f),n01) , dot(pf-(float2)(1.f,1.f),n11),
                         sx, sy);
}

となっており、パーティクルの座標と4点少しずらした座標でbilerpして求めます。
またランダムテーブル自体も

//host
float spin_t = 0.5f*0.03f/0.1f*time_;
for(unsigned int i=0; i<RANDOM_SIZE; ++i){
 float theta=rnd_table_spin_[i]*spin_t;
 float c=std::cos(theta),s=std::sin(theta);
 rnd_table_[i][0]= c*rnd_table_orig_[i][0]+s*rnd_table_orig_[i][1];
 rnd_table_[i][1]=-s*rnd_table_orig_[i][0]+c*rnd_table_orig_[i][1];
}


このように円を書くように更新していく事で、うずまくような動きになります。(つまりCurl)
float turb=g*d*0.0026f*noise((px-t*wind_velocity)/0.1f,rt,rti);
               ^^^^^^^

ノイズゲインを上げることによってこの動きは顕著になります。(前回の↑部分がゲイン)

といった所で前回の説明忘れ部分は終了です。
次にパフォーマンス比較を忘れていたので行いました。
100k particles
GPU          :   10ms
CPU single  : 183ms
CPU omp    :   93ms

CPU singleはシングルスレッド。CPU ompはOpenMPを用いた並列化の結果になります。
大体想定どおりの結果です。
CPU ompが若干遅いですが2coreのアレなCPUを使ってるので現行の4core辺りなら40-60ms位に落ち着くのではないでしょうか。

Curl Noise自体が並列化し易く、かつ入力データが多い例なのでOpenCLでの高速化が上手くはまるアルゴリズムでしたのでこのような(理想的な)結果に落ち着きましたが、何でもかんでもGPGPUで高速化できるものではありません。
例えば入力が多く、並列化が容易であっても分岐が多くなるとGPUでの高速化は上手くはまらないのでアルゴリズム審美眼を磨いて、「これはGPUでうまくいく!」といった勘を養うことがGPGPUでは重要ではないかなぁといった所で僕のノープランgdgd GPGPU Advent Calenderを終了しようと思います。
atnd立ち上げた山田さんに多謝!

0 件のコメント:

コメントを投稿