DirectXの話 第156回

正式版DirectX Raytracingをいじる

18/11/17 up

DirectX Raytracingが正式版になりました

10月のWindows UpdateによってDirectX Raytracingが正式版になりました。

このため、これまで使っていたFallback Layerはフェードアウトしていくことになります。

まだ現段階では使用可能なようですが、いつ使えなくなるかわかりません。

というわけで、DXRを使いたい方は NVIDIAさんの Geforce RTX を購入して正式版をいじりましょう。

サンプル動作環境

サンプルの動作には以下の環境が必要になります。

  • Windows 10 October 2018 Update (RS5)

  • NVIDIA Geforce RTXシリーズのGPU (Volta世代でもいけるはず)

      • ドライバは最新ならOKのはず

  • Windows SDK 17763以上

  • Visual Studio 2017

サンプルは以前と違ってDirect3D12サンプルに統合しました。

DXRSampleはFallback Layerを追うのが面倒なことと、サンプル用ライブラリがそのまま使えたのでDXR用に拡張しました。

D3D12Sample

こちらの Sample009 が今回のサンプルになります。

解説について

DXRの概念部分は以前までの記事で書いているので、ここで再度書くことはしません。

また、CEDEC 2018での NVIDIA社の竹重さんのセッションが大変わかりやすいと思います。

今回の記事では以前までの Fallback Layer との違いを簡単に解説していきます。

デバイス初期化周り

Fallback Layer では EnableD3D12ExperimentalFeatures() 命令を使って実験的フィーチャーを有効化する必要がありましたが、正式版では不要です。

また、Fallback Layer 用のデバイスを ID3D12Device から作成する必要がありましたが、正式版では QueryInterface() で簡単に必要なデバイスを取得できます。

SampleLib12/src/device.cpp (84) からが対象のコードです。

D3D12_FEATURE_DATA_D3D12_OPTIONS5::RaytracingTierD3D12_RAYTRACING_TIER_NOT_SUPPORTED なら環境がDXRに対応していません。

対応している場合は ID3D12Device5QueryInterface() します。

同様に Command List も QueryInterface() します。

こちらは ID3D12GraphicsCommandList4 です。

SampleLib12/src/command_list.cpp (25) からが対象のコードとなります。

Root Signature周り

Root Signature は普通のD3D12の作成方法でそのままいけます。

Fallback Layer の初期の頃は Descriptor Table を使用するとうまくいかない場合がありましたが、正式版は問題なく動作します。

注意点としては Top Acceleration Structure の取り扱いについてです。

Global Root Signature を通してシェーダに渡すことになるのですが、どうも Descriptor Table は使用できないようです。

試してみたのですが無理でした。

D3D12_ROOT_PARAMETER_TYPE_SRV を使って GPU Virtual Address を直接渡すようにしましょう。

Pipeline State周り

こちらも Fallback Layer とほとんど同じです。

最終的に作成されるオブジェクトは、ID3D12StateObject になっています。

以前は Prototype が付属していたオブジェクト名でしたが、正式版になったので正式名に変わってます。

初期化の手順には変更がなく、Subobject を複数用意するのは変わりません。

できるだけ簡単に生成できるようにするため、sl12::DxrPipelineStateDesc というクラスを作成しました。

Subobject を vector で保存するようにしており、Local Root Signature のポインタ対応を最終的に Resolve できるようにしています。

Geometry周り

頂点バッファ、インデックスバッファは普通のShader Resourceとして使用するバッファとして作成します。

今回のサンプルではテクスチャマッピングを行うため、頂点座標、UV座標、インデックスの3つのバッファを作成しています。

また、シェーダで対応するUV座標を計算するため、UV座標とインデックスのバッファにはViewを作成しています。

Accelration Structure周り

Acceleration Structure (以下 AS)の生成部分にも Fallback Layer からの変更点はほとんどありません。

ビルドする際のコマンド発行時に Descriptor Heap を設定する必要がなくなった、というくらいでしょうか。

SampleLib12には Bottom AS と Top AS のクラスを用意していますので、比較的簡単に生成できるようになっています。

Shader Table周り

SampleLib12でラップしていない部分です。

どういう設計にしようかまだ悩んでるので、サンプルいくつか作ってから決めようかなと。

こちらも Fallback Layer から大きな変化はないですが、MSのサンプルで Shader ID と Descriptor Handle の部分にアラインメントを入れるようになってました。

これに従って自分のサンプルでもアラインメントを入れるようにしています。

Local Root Signature で使用する Descriptor は4つで、テクスチャ用、サンプラ用、UVバッファ用、インデックスバッファ用となります。

Let's Raytracing!

実際にレンダリングする部分にも特に変化はありません。

Fallback Layer と同様に DispatchRays() 命令を呼び出せばOKです。

Shader周り

Fallback Layer のサンプルではやらなかったですが、今回はテクスチャサンプリングを行いました。

UV計算は当然 barycentrics の値を利用して自前で計算する必要があります。

また、テクスチャの Sample() 命令は使用できません。これはミップマップ計算に必要な傾きを計算できないためです。

ミップマップレベルはテクスチャが画面に表示される際に必要な解像度から計算されます。

必要な解像度はどのように計算するかと言うと、隣接ピクセルとの間でUV座標の変化量(UV座標のddx, ddy)を求め、これと実際のテクスチャ解像度から、1ピクセルずれたときにテクスチャ的に何ピクセル進むかを計算します。

これによって適切なミップレベルを求めることになるわけです。

ピクセルシェーダでテクスチャサンプリングする場合、ddx, ddy は2x2のピクセル範囲内で自動的に計算され、これをもとにミップレベルが自動的に計算されます。

そのため、Sample() 命令を使用できるわけです。

しかし、ピクセルシェーダ以外のシェーダでは残念ながらddx, ddyが求められないため、Sample() 命令は使用できないというわけです。

もちろん、この制約はDXRでも同様ですが、ミップマップを使用したい場合はddx, ddyを計算する方法もあります。

この方法についてはMSのMiniEngineのサンプルを見るのが手っ取り早いでしょう。

ある程度の事前計算をしておけば更に高速化できるかと思います。

また、より詳しく知りたい方は最近 Free になった Physically Based Rendering : From Theory to Implementation に詳しく書いてあります。

Sample() 命令は使えないものの、ddx, ddyを自前で計算できれば SampleGrad() 命令で同じ結果を得られます。

ミップマップを使用しない場合は SampleLevel() 命令を使いましょう。今回のサンプルではこれを使っています。

最後に

まだまだ調べきれていない部分も多いですが、概ね Fallback Layer と同じように正式版DXRも使用できることが確認できました。

今後はもっとレイトレらしいレイトレのサンプルや、多分現実的なラスタライザとの合わせ技のサンプルなんかを作成していく予定です。

特に TraceRay の深度をどこまで増やせるかとか調べてみたいところですし。

気長にお待ち下さい。

待てない!って人はサンプル作って公開してくれてもいいのよ?