はじめに
Ray Tracing in One Weekendの完成物相当のものを作ってみました。いくつかの変更点や気になったところを書き留めておきます。
OpenCL_Simple_RayTracingにソースコードを置いておきます。
次のような画像(1200*675 pixel, 50回反射, 500サンプル)をメモリ上に生成するのにIntel UHD 630で215秒ほど、AMD Radeon Vega 56で42秒ほどかかりました。

GPUで処理する際に変更したところ
- 再帰呼出をなくす
- double型のところをfloat型にする
- 長時間のGPUの占有を避ける
- 乱数の生成をワークアイテムごとに行う
- その他
再帰呼出をなくす
OpenCL Cでは再帰呼出は非サポートです。
Ray Tracing in One Weekendのプログラムではレイの反射・屈折のたびに再帰呼出をしていますが、ここをループで済むよう書換えました。反射する割合を掛け合わせていくだけです。
double型のところをfloat型にする
double型はfloat型に比べて概ね演算に16倍ほど時間がかかるようです。

AMD APP SDK OpenCL Optimization Guide より抜粋
長時間のGPUの占有を避ける
ドライバー開発中の TDR のテストとデバッグにあるようにレジストリに手を加えれば問題はなさそうなのですが、極力GPUを掴みっぱなしにしないほうがよさそうなので。
アンチエイリアシングのために同一ピクセルにレイを何度も撃ち足し合わせるところを、カーネルの呼出ごとに1回のみにしました。
乱数の生成をワークアイテムごとに行う
OpenCLのはじめかたなどで記述したものを用いました。
乱数を書きこむ領域をダブルバッファリングして、デバイスの処理中にホストで乱数を生成するという手でもよかったかもしれません。
その他
乱数生成の際に棄却法としているところを、別の方法に書換えました。
inline vec3 random_in_unit_disk() {
while (true) {
auto p = vec3(random_double(-1,1), random_double(-1,1), 0);
if (p.length_squared() < 1)
return p;
}
}
inline vec3 random_in_unit_sphere() {
while (true) {
auto p = vec3::random(-1,1);
if (p.length_squared() < 1)
return p;
}
}
float2 GetRandamVectorinUnitDisk(uint *random_state)
{
float theta = GetRangedFloat(random_state, -M_PI_F, M_PI_F);
float r = sqrt(GetRandomUnitFloat(random_state));
return (float2)(r*cos(theta), r*sin(theta));
}
float3 GetRandomVectorInUnitSphere(uint *random_state)
{
float phi = GetRangedFloat(random_state, 0.0f, 2 * M_PI_F);
float z = GetRangedFloat(random_state, -1.0f, 1.0f);
float r = GetRandomUnitFloat(random_state);
float x = cbrt(r) * sqrt(1.0f - z*z) * cos(phi);
float y = cbrt(r) * sqrt(1.0f - z*z) * sin(phi);
z = cbrt(r) * z;
return (float3)(x, y, z);
}
感想など
Ray Tracing in One Weekendの原文を邦訳したものには古いものもあるので、原文にも目を通すのがよさそうです。
今回のように分岐の中身が多いプログラムではパフォーマンスが低くなりがちかもしれません。
レイを撃って得た色を加算する際、その回数が多すぎると情報落ちにより加算が反映されなくなります。緩和策はあるのですが煩雑なものになります。
GPUを連続して長時間占有するとOSの介入によりデバイスのリセットが発生するかもしれません。予防策として小分けにした領域ごとに処理するくらいならまだ易しいのですが、反射屈折ごとにカーネルを走らせるようにすると難しくなりそうです。
参考文献
そら飛ぶタピオカ 球表面・球内に一様分布する点をいっぱい生成する
KAYAC engineers' blog 円や球面にランダムに点を散らしたい〜高校数学を使って分布関数を作る〜