1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

OpenCLを使って簡易なレイトレーシングをする

Posted at

はじめに

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

GPUで処理する際に変更したところ

  • 再帰呼出をなくす
  • double型のところをfloat型にする
  • 長時間のGPUの占有を避ける
  • 乱数の生成をワークアイテムごとに行う
  • その他

再帰呼出をなくす

OpenCL Cでは再帰呼出は非サポートです。
Ray Tracing in One Weekendのプログラムではレイの反射・屈折のたびに再帰呼出をしていますが、ここをループで済むよう書換えました。反射する割合を掛け合わせていくだけです。

double型のところをfloat型にする

double型はfloat型に比べて概ね演算に16倍ほど時間がかかるようです。

GCN_Instruction_Throughput.png
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 円や球面にランダムに点を散らしたい〜高校数学を使って分布関数を作る〜

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?