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立ち上げた山田さんに多謝!