DirectXの話 第157回

Ray Tracing in a WeekendをDXRで実装する

19/01/12 up

Ray Tracing in a Weekend

現在はフリーでダウンロードできるようになっている書籍です。

レイトレを学び始めるのには最適な書籍ではないでしょうか?

内容としては、C++を使って上のような画像を作成するための各種解説を行っています。

内容は短いですが、サンプルコードもあり、且つわかりやすく解説してくれているので基礎は確実に学べます。

名前のとおり、週末を1回使えば終わる程度の内容ですし、セクションごとに結果が見られるので飽きずに進められるでしょう。

これからレイトレをやりたい人はまずここから始めるとよいのではないでしょうか。

DLは以下の RealTimeRendering.com から行えます。

http://www.realtimerendering.com

DXRで実装してみる

特に難しいことはしていないのですが、この書籍の内容をDXRで実装してみました。

サンプルはいつもどおりにGitHubにコミット済みです。

サンプル

Sample010が対象のサンプルです。

もちろん、正式版のDXRを使っているので、GeforceRTXが必要になります。

ビルド自体はVS2017と最新のWindowsSDKがあれば通るはずです。

実装するにあたって難しいことはあまりないです。

154回155回でやったように、Intersection Shader を利用して球のレイ衝突判定を行っています。

今回新たにやっていることといえば、マテリアルによる処理の変更、乱数の使用、ラスタライザとの併用です。

マテリアルによる処理の変更

これは以前にもやっているといえばやっているのですが、今回はかなりサボり気味です。

シェーダコード ray_trace.lib.hlsl ファイルを見ていただければわかると思いますが、Closest Hit Shader は1つだけです。

中身はマテリアルによる処理の変更は分岐しているだけです。

単純な分岐ではありますが、これくらいのコードであればシェーダテーブルを分けて処理するより分岐の方が高速に動くのではないかと思ったり思わなかったり…

実際に比べてないのでわかりませんが。

各マテリアルによる処理は書籍の内容と同じです。

特筆すべき事は乱数関係くらいかな?

そちらは後ほど。

レイのバウンス回数ですが、こちらは MaxReflCount によって制限しています。

レイトレースの再起回数は PipelineState を作成する際に D3D12_RAYTRACING_PIPELINE_CONFIG::MaxTraceRecursionDepth で指定します。

指定できる数値は 0~31 です。

サンプルでは最大の31を指定しています。

最初に Ray Generate Shader によって TraceRay が行われ、その際に payload.refl_count は 0 に設定されますので、MaxReflCount は 30 に設定されています。

自分の環境では、これ以上に TraceRay を発行すると、最初数フレームを描画したあとにアプリケーションがクラッシュしました。

仕様どおりの動作なのかは不明ですが、再帰的にレイトレースしたい場合は注意しましょう。

乱数の対応

書籍の中では各所で乱数が使用されています。

CPUで乱数を使いたい場合は rand() 関数やSTLの乱数なんかを使用すれば難しくはないのですが、GPUではこの手の命令が使用できず、乱数のシードを指定して生成するのが精一杯です。

たいていシードは時間軸を利用して変更するのですが、もうちょっと別の方法ないかなと。

今回は2種類の乱数を用いています。

Lambert マテリアルや Metal マテリアルで使用するベクトルの乱数については、書籍とは違って Hammersley を使用しています。

半球方向に一様乱数的にベクトル生成する場合によく使われます。

ベクトル関係は全部これ。

この手法で半球方向のレイを生成したあとは中心軸(法線や反射ベクトル方向)にクオータニオンで回転してワールド空間のレイの方向として扱っています。

法線方向に向ける場合はタンジェントも使ったほうがいいとは思うのですが、タンジェントを設定していないので致し方なし。

もう1つの乱数は乱数テーブルを事前に作成することで対応しています。

乱数テーブルを順番にたどっていくだけという、乱数としてはどうなの?って感じではありますが、さほど悪くない感じですかね。

コードはこんな感じ。

65536個の 0~1 までの小数点をテーブルとしておき、テーブルを参照するインデックのバッファを常にインクリメントして上から順番に持ってくるだけです。

ひどい実装ではありますが、さほど問題ないのでOK!

ラスタライザとの併用

併用と言っても、ラスタライザでメッシュを描画したりしているわけではありません。

ImGui の描画とレイトレ結果をSwapchainにコピーするための描画です。

前回までのDXRサンプルはSwapchainに CopyResource 命令を使ってコピーしていましたが、今回はF32フォーマットを使用しているため直接コピーが出来ません。

F32フォーマットを利用している理由は複数フレームでレイを飛ばしているからですね。

複数フレームでレイを飛ばし、その結果の平均を取って最終画像としています。

そのため、8ビットフォーマットではどうしようもないので浮動小数点バッファを使うようにしました。

ImGuiの描画もそのままですね。

カメラを動かせるようにしたので、そのGUIを表示したかっただけです。

なお、最初に描画したときはうまくいかなくて、なんでだぁ…って思ってたら、Viewportを指定するのを忘れてたというオチでした。

みんなも気をつけようね!

次回

どうしようかなと思ってるんですが、メッシュをラスタライザで実装、シャドウやAOをレイトレで、というのを考えてます。