DirectXの話 第154回

DirectX Raytracing FallbackでCornell Box その1 18/07/04 up

今回は上の画像のようなCornell Boxを表示するまでの話をします。

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

https://github.com/Monsho/DXRSamples

今回のサンプルで実装している技術は以下のとおりです。

    • トライアングルメッシュを使用せずにすべてプロシージャルメッシュで描画

    • Hit Groupを処理によって場合分け

    • Miss Shaderも処理によって場合分け

DXRは Hit Group 周りが少し複雑、というか、いくつかの実装方法を選択できるようになっています。

今回実装した方法もそんな実装のうちの1つにすぎないので、興味があったら別の実装を試してみても良いでしょう。

Intersection Shaderについて

さて、いきなり出てきた Intersection Shader という単語です。

このシェーダはレイとメッシュの衝突判定を取るためのシェーダです。

ここまでのサンプルではこのシェーダは実装しませんでしたが、それはDXRが内部でトライアングルメッシュとレイの衝突判定シェーダを実装しているからです。

今回はトライアングルメッシュを使用していないため、この衝突判定シェーダを使用できません。つまり、自前で実装する必要がある、ということです。

今回のサンプルには Intersection Shader が2種類存在しています。

1つは球とレイとの判定、もう1つはAABBとレイとの判定です。

とりあえず、球とレイとの判定を行っている Intersection Shader を見てみましょう。

[shader("intersection")]void IntersectionSphereProcessor(){ Instance instance = Instances[InstanceIndex()]; float3 w_origin = WorldRayOrigin(); float3 w_dir = WorldRayDirection(); float3 ray_origin = w_origin + w_dir * RayTMin(); float3 ray_dir = w_dir; float Thit = 0.0; float3 normal; float3 center = instance.mtxLocalToWorld._m30_m31_m32; float radius = instance.mtxLocalToWorld._m00; if (IntersectToSphere(center, radius, ray_origin, ray_dir, Thit, normal)) { MyAttribute attr; attr.normal = normal; ReportHit(Thit, 0, attr); }}

Intersection Shader となる関数の前に、この関数が Intersection Shader である旨を記述しています。

Instance という構造体はシェーダ内で定義されているもので、メッシュの姿勢を示す行列とメッシュのカラーを格納しています。

ここでは、衝突したメッシュのインスタンス番号から、グローバルルートシグネチャに設定されている Structured Buffer に設定されているデータを取得しています。

現在のレイの原点、レイの方向、レイの長さを用いて Instance が示す球とレイとの衝突判定を行うのが IntersectToSphere() 関数です。

この関数は自前で用意したもので、衝突している場合に true が返るようになっています。

また、第5引数に衝突した際のレイの長さが、第6引数には衝突した位置のメッシュの法線が返ります。

衝突判定が取られた場合、ヒットしたことを報告する必要があります。

そのための関数が ReportHit() です。

この命令は Intersection Shader からのみ呼び出しが可能で、Any Hit Shader、及び Closest Hit Shader を呼び出すことになります。

第1引数には衝突した際のレイの長さを指定します。

Closest Hit Shader内では、この長さを RayTCurrent() 命令で取得することができ、レイの原点(WorldRayOrigion()関数)、レイの方向(WorldRayDirection()関数)と一緒に計算することで衝突点を求めることができます。

第2引数にはヒットの種別を指定します。

この値は 0~127 で指定し、Any Hit Shader、及び Closest Hit Shader 内で HitKind() 関数を用いて取得することができます。

この値はユーザが好きなように使用していい値ですので、ヒットしたメッシュの種類などを送ることもできます。

なお、DXRに組み込まれているトライアングル-レイの衝突判定命令では HitKind() 命令で衝突したトライアングルの表面か裏面かを取得することができます。

第3引数は Closest Hit Shader に引き渡すことができる属性パラメータです。

属性パラメータはユーザが定義することが可能で、パイプラインステートオブジェクトを生成する際に Shader Config で設定を行います。

D3D12_RAYTRACING_SHADER_CONFIG.MaxAttributeSizeInBytes 変数で指定できるので、Payload のサイズと同じように設定しましょう。

今回はここに衝突したメッシュの法線情報をもたせるようにしました。

衝突箇所は Closest Hit Shader 内でも計算でき、そこから法線も求められますが、属性の動作確認のためにここで求めた法線を渡すようにしています。

Intersection Shader を使用するにはこのシェーダを Hit Group に設定する必要があります。

第150回で説明したとおり、Hit Group には Intersection Shader、Any Hit Shader、Closest Hit Shaderの3つをそれぞれ1つずつ設定することができます。

Intersection Shader はここに設定することになりますが、あるメッシュ(ジオメトリ)に対して1つのレイが処理できる Hit Group は1つだけです。

どういうことかというと、TraceRay() 命令でレイを飛ばすと、TopAS内でレイとメッシュの衝突判定を大まかに取ります。

衝突してるかも?というメッシュが見つかった場合、呼び出すべき Hit Group がレイやインスタンスの情報から選択されます。

DXRはまず、この Hit Group の Intersection Shader を呼び出し、実際に衝突しているかどうかを調べます。

衝突している場合は ReportHit() が呼び出され、同じ Hit Group 内のAny Hit Shader、Closest Hit Shaderが呼ばれます。

Intersection Shader はそもそもそのメッシュの形状を判定するための命令と言ってもいいでしょう。

それに対して Closest Hit Shader はどちらかというとメッシュの形状ではなくマテリアルとなります。

つまり、形状は同じだけどマテリアルが違う、という場合、それぞれの組み合わせに応じた Hit Group を作成しなければならないという問題が存在します。

これについては回避方法も存在しているようなのですが、まだ検証を行っていないのでなんとも言えない部分です。

BottomASを変更する

前回までのサンプルでは、BottomAS 生成時に指定するジオメトリ記述子のパラメータ、D3D12_RAYTRACING_GEOMETRY_DESC.Type には D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES を指定していました。

今回は球やAABBのような、プロシージャルに対応できるメッシュを使用しているため、特にトライアングル情報というものは存在しません。

そのような場合は D3D12_RAYTRACING_GEOMETRY_TYPE_PROCEDURAL_PRIMITIVE_AABBS を指定します。

これによって、ただのAABBとしてBottomASには登録されるので、Intersection Shader で形状を規定することが可能になります。

今回、BottomAS は2つ作っています。

まず、2つ配置されている球を指定するもので、ソースコード内では g_pPropBottomAS_ というオブジェクトになっています。

ここには1つのAABBが入っているだけで、球はインスタンスによって設定されることになります。

もう1つの BottomAS は g_pInnerBoxBottomAS_ オブジェクトで、インナーボックス、つまり箱の中身を表示させるようなオブジェクトとなります。

実際にはただのAABBが5つ存在しているだけです。

こちらも球と同じでインスタンスを5つ設定しても良かったのですが、球とは別の形で実装してみようと考えたので、BottomAS には5つのAABBを、1つのジオメトリとして登録しています。

BottomAS に複数のAABBを割り当てる方法はいくつかあります。

D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC.NumDescs に1より大きな値を設定し、D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC.pGeometryDescsNumDescs に設定した数の配列のジオメトリデータを渡す方法が1つ。

この方法の場合にAABBごとに処理を分けたい場合は、AABBごとに Shader Table を指定してあげる必要があります。

Shader Table のインデックスがどのように求められるかについては前回の記事を参照してください。

もう1つの方法は D3D12_RAYTRACING_GEOMETRY_DESC.AABBs.AABBCount に個数を指定する方法です。

この場合、BottomAS に対応する Shader Table は1つでOKです。

AABBごとに処理を分けたい場合は、シェーダコード内で PrimitiveIndex() 命令を使用すれば、衝突したAABBの番号を取得できます。

今回のサンプルでは後者で対応していますが、MSのサンプルでは前者を用いています。

ASの生成は InitAccelerationStructure() 関数内で行っています。

さほど難しいことはしていないので解説は行いませんが、わかりにくい部分などありましたらお伝えいただければ対応します。

一旦、今回の記事はここまでにしておきます。

後半では Shader Table 周りの修正点などを解説していきたいと思います。