書きかけの記事です。

まずはレイトレがどんなものかを知らないといけない。カメラからレイを飛ばして物体との交差判定をして何次反射まで計算させて…というのは知っているけど具体的に同実装するのかが分からないという知識レベルから本稿を書く。

参考:wikipedia – Raytracing

memo

  • path tracing 下記の手法
    • soft shadow, motion blur, dof, indirect lighting
  • ライトではなくカメラからレイを飛ばして計算するのはライトからのレイは必ずしもカメラに入るわけではないので最初からカメラからレイを飛ばす
  • ある距離交差判定が無ければそのレイの計算は中断する

TODO: Q. カメラからレイを飛ばしてオブジェクトに当たる。その時の色の計算って具体的にどうやるの?

仮説:更にレイを反射させてそのレイが点光源に当たればその点光源の色になる。最初に当たったオブジェクトの材質に合わせてレイの強度を変えれば従来のラスタライズ型と同じ色になりそうな予感はするよね。

 

nvidia tutorial – Part1

参考:https://developer.nvidia.com/rtx/raytracing/dxr/DX12-Raytracing-tutorial-Part-1

7. overview of the system

memo

  • Acceleration Structure
    • ジオメトリ情報を保持
    • 目的は交差判定を高速に計算するため
    • Top Level/Bottom Level に分かれており TLAS/BLAS と呼ばれる
    • Acceleration Structure: AS
  • レイトレパイプラインはシェーダプログラムを保持
    • シェーダプログラムに紐づいた「関数名」も一緒に保持している
    • TODO: Q. 何のための関数?別に何かする関数があるのか?
      • またシェーダプログラム間でデータのやり取り方法の情報を持っている
  • Shader Binding Table (用途不明だけど)下記の様なバインディング構造になっている
    • ShaderProgram <- Func Name -> RayGen – Data
    • ShaderProgram <- Func Name -> Miss – Data
    • ShaderProgram <- Func Name -> Hit – Data
    • 仮説
      • オブジェクト毎に Hit した時に起動する ShaderProgram を Func Name で関連付けている

8. Acceleration Structure

memo

  • BLAS
    • 頂点データを抱えている
    • 頂点データとその数
    • D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL
    • D3D12_RAYTRACING_GEOMETRY_DESC
      • 頂点バッファ、インデックスバッファ、変換行列の情報を持つ
  • TLAS
    • BLAS とそのトランスフォーム行列から成る
    • TLAS は 1 つの BLAS から複数インスタンス化できる。つまり、1 描画オブジェクトを複数位置を変えて描画する場合でも BLAS 1 つで TLAS に与える Transofrm Matrix を変えるだけで良い。
    • TLAS を生成するための入力は BLAS の Resource(D3D12上の?)と変換行列
    • TLAS のインスタン毎にでスクリプタが必要
    • DONE: 隠蔽されている scratch and rsult buffers の用途は?
      • シーンの複雑さに応じて変わる一時情報(ドライバ依存だろうけど)
  • Acceleration Structure
    • a

nvidia tutorial – Part2

参考:https://developer.nvidia.com/rtx/raytracing/dxr/DX12-Raytracing-tutorial-Part-2

D3D12 自体は知っている前提のメモ

2. Shading Pipeline

memo

  • RootSignature が Ray/Miss/Hit それぞれで必要
  • Library は idxcblob で Ray/Miss/Hit それぞれ用意
  • 普通にパイプラインステートを 1 つ用意
  • id3d12stateobjectproperties というのが必要
    • リソーステーブルとシェーダライブラリ内の個々のシェーダプログラム関連付けの identifier
    • 名前引きでリソースを問い合わせるための仕組み?
  • RayGen シェーダプログラム
    • TLAS, 出力イメージ(出力バッファ)へのアクセスが必要
    • TLAS -> D3D12_DESCRIPTOR_RANGE_TYPE_SRV
    • 出力イメージ -> D3D12_DESCRIPTOR_RANGE_TYPE_UAV
    • 結局使うリソースは上記 2 つだけ
  • Hit シェーダプログラム
    • RayGen のレイのペイロードの色を返すだけなのでリソースは不要
  • Miss シェーダプログラム
    • 同上
  • Pipeline 生成
    • Pipeline にそれぞれの RootSignature/shader code/pipeline characterristic をバインドする
    • IDxcBlobEncoding データ構造を IDxcBlob から作る必要がある
    • D3D12_STATE_SUBOBJECT
    • DLL と同様に dxc がコンパイルしたライブラリの関数を名前指定でパイプラインにアタッチする
    • シンボル名はシェーダ内で [shader(“xxx”)] で指定できる
  • ヒット判定に使われるシェーダは the intersection shader, the any hit shader, and the closest hit shader の 3 つが使われるがこれらはヒットグループとして 1 つのグループで扱われる
  • ヒットグループのパイプラインへのバインドはヒットグループ名とそれにバインドするシェーダ名を入力として D3D12_STATE_SUBOBJECT を構築しパイプラインにバインドする。
  • HLSL に対する操作
    • HLSL をコンパイルしてライブラリ化
      IDxcLibrary::CreateBlobWithEncodingFromPinned()
      IDxcBlobEncoding
      IDxcCompiler::Compile()
      IDxcBlob
    • Pipeline に D3D12_STATE_SUBOBJECT としてライブラリ内の関数名([shader(“xxx”)])を指定してバインドする
  • シェーダへの RootSignature のバインド
    • HitGroup は 1 つの RootSignature を共有する(グループ化されているのだからそうせざる負えない)
    • その他のシェーダはそれぞれ RootSignature をバインドできる
      シェーダへの RootSignature のバインド疑似コード

       pipeline.AddRootSignatureAssociation(m_rayGenSignature.Get(), {L"RayGen"});
        pipeline.AddRootSignatureAssociation(m_missSignature.Get(), {L"Miss"});
        pipeline.AddRootSignatureAssociation(m_hitSignature.Get(), {L"HitGroup"});
  • ペイロードについて
    • 基本的な用途は Hit/Miss シェーダが RayGen シェーダに「色を返す」用途で使われる
    • データの受け渡しはバッファで行われる
    • パフォーマンス的にペイロードサイズはできるだけ小さくする
  • インターセクションシェーダの attribute について
    NOTE: このサンプルでは組み込みのインターセクションシェーダを使っている
    NOTE: barycentric coordinates 重心座標(重心を原点とする座標)

    • トライアングルの重心座標系の vec2 を返している
  • 再帰回数について
    • TraceRay() を何回やるか?
    • パフォーマンス的には少ない方がいい
    • もし増やす場合は TraceRay() のループ数を増やした方が良い
    • TODO: TraceRay() って具体的に何?
      • シェーダのレイトレース用関数名
      • TraceRay() をコールするシェーダは TLAS にアクセスできないといけない
  • Pipeline にペイロードサイズ、インターセクション attribute サイズ、再帰回数を教えてやる必要がある
    疑似コード:

     pipeline.SetMaxPayloadSize(4 * sizeof(float)); // RGB + distance
      pipeline.SetMaxAttributeSize(2 * sizeof(float)); // barycentric coordinates
      pipeline.SetMaxRecursionDepth(1);
  • a

3. Creating Resources

memo

  • レイトレは UAV に結果を書き込んでディスプレイバッファにコピーする
  • TraceRay() をコールするシェーダは TLAS にアクセスできないといけない
    • そのための SRV が必要
  • RayGen シェーダが必要なリソース
    • TLAS
      生成疑似コード:

        D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc;
        srvDesc.Format = DXGI_FORMAT_UNKNOWN;
        srvDesc.ViewDimension = D3D12_SRV_DIMENSION_RAYTRACING_ACCELERATION_STRUCTURE;
        srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
        srvDesc.RaytracingAccelerationStructure.Location =
            m_topLevelASBuffers.pResult->GetGPUVirtualAddress();
        // Write the acceleration structure view in the heap
        m_device->CreateShaderResourceView(nullptr, &srvDesc, srvHandle);
    • 出力バッファ
      ただの fb サイズと同じサイズの UAV

4. Create Shader Binding Table

memo

  • Shader Binding Table
    • ジオメトリインスタンスとシェーダプログラム、シェーダリソースを関連付ける
  • サンプルの 3 エントリーに内訳

    • RayGen シェーダプログラム/Miss シェーダプログラム/ヒットグループ
      RayGenIdentifier
      HeapStartPointer
      MissIdentifier
      HitGroupIdentifier
    • RayGen 自体は出力バッファと TLAS にアクセスできないといけない
    • ピクセル毎に RayGenIdentifier がエントリーポイントとして呼び出される
    • HeapStartPointer はシェーダからのリソースアクセスに使われる
    • 例を打つ時に HeapStartPointer から TLAS にアクセスする
    • ミス時は MissIdentifier で指定されるシェーダが呼び出される
    • ヒット時は HitGroupIdentifier で指定されたシェーダグループが呼び出される
    • そして RayGen シェーダにバッファを介して計算結果が渡されてバッファに最終結果を書き出す
  • obj0, 1, 2があり 2 だけリソースが無い場合の SBT 構造
  • SBT 内の各エントリーはプログラムの種別毎に最大のエントリー数でパディングを入れないといけない。なので上図で HitGroup2 はパディングを 1 エントリー入れている。
  • ヒットグループインデックスとジオメトリの関連付けについて
    • 内部的には D3D12_RAYTRACING_INSTANCE_DESC の InstanceContributionToHitGroupIndex にマップしている
  • SBT の構築は名前とポインタを入力にとる
      // The ray generation only uses heap data
      m_sbtHelper.AddRayGenerationProgram(L"RayGen", {heapPointer});
    
      // The miss and hit shaders do not access any external resources: instead they
      // communicate their results through the ray payload
      m_sbtHelper.AddMissProgram(L"Miss", {});
    
      // Adding the triangle hit shader
      m_sbtHelper.AddHitGroup(L"HitGroup", {});
  • a

5. Calling the Raytracer

memo

  • D3D12_DISPATCH_RAYS_DESC が従来には無い概念
    • SBT をどう解釈するか、処理するピクセル数を記述する
  • DispatchRays(D3D12_DISPATCH_RAYS_DESC)
  • a

6. Simple RayGen and HitShader – RayGen

memo

  • RayGen シェーダの処理
    • DispatchRaysIndex()
      座標取得 in pixel
    • DispatchRaysDimensions()
      フレームバッファサイズ
    • レイの情報を構築 – ここでは RayDesc と呼ぶことにする
      概ね下記の情報をシェーダ内で作る

      // Define a ray, consisting of origin, direction, and the min-max distance values
      RayDesc ray;
      ray.Origin = float3(d.x, -d.y, 1);
      ray.Direction = float3(0, 0, -1);
      ray.TMin = 0; // near clip
      ray.TMax = 100000; // far clip

      TraceRay() の引数に必要。

    • TraceRay() をコール
      • Acceleration Structure をなめて HitGroup か MissShader を呼び出す
      • レイフラッグ
        ヒット時の挙動をフラグによって制御できる

        • RAY_FLAG_NONE
          デフォルト値
        • RAY_FLAG_FORCE_OPAQUE
          ヒットシェーダを呼び出さない
      • InstanceInclusionMask
        特定のオブジェクトだけレイトレを行わない

        • デフォルト値は 0xFF でこの時除外されるオブジェクトは無い
      • RayContributionToHitGroupIndex
        • ヒットグループを探す時の挙動に影響する
        • 複数種類のレイを使う時に機能する
        • オブジェクトに対して複数のヒットグループをアタッチできる
        • eg. 通常の計算と影の計算用
        • レイの種類により同じオブジェクトでも SBT のヒットグループを参照する時にオフセットを加えて使用するヒットグループを選択できる
      •  MultiplierForGeometryContributionToHitGroupIndex
        • ヒットグループを探す時の挙動に影響する
        • SBT 内のオフセットはオブジェクトのインスタンス ID から決まる
        • このインスタンス ID は Acceleration Structure に突っ込まれた順番が ID になっている?
        • The offsets in the SBT can be computed from the object ID, its instance ID, but also simply by the order the objects have been pushed in the acceleration structure. This allows the application to group shaders in the SBT in the same order as they are added in the AS, in which case the value below represents the stride (4 bits representing the number of hit groups) between two consecutive objects.
        • 結局 SBT にアクセスする時のストライド計算のパラメータと考えてよい?
      • コール後出力バッファに結果が出力される

6. Simple RayGen and HitShader – HitShader, MissShader

memo

  • HitShader
    • DONE: Q. HitShader はヒット判定自体を覆せるのか?
      • 多分できない。
    • 本サンプルではビルトインの triangle intersection shader から出力される vec2 の baycentric 座標を ClosestHit() 関数で使用している。
    • 単純にこの値をペイロードとしてバッファに書き出しているだけ。
  • MissShader
    • 適当なペイロードの値を出力しているだけ。

7. Using the Vertex Buffer

memo

  • 通常ヒットグループの役割は交差アトリビュートを受け取りペイロードを書き出すだけだが SBT に SRV を追加しておくと VBO にアクセスできる。まずは Hit Root Signature にサブリソースを追加する。
    rsc.AddRootParameter(D3D12_ROOT_PARAMETER_TYPE_SRV);
  • SBT に VBO を追加
    疑似コード:

    m_sbtHelper.AddHitGroup(L"HitGroup", {(void*)(m_vertexBuffer->GetGPUVirtualAddress())});
  • HLSL に VBO 用のリソースを追加
    struct STriVertex
    {
      float3 vertex;
      float4 color;
    };
    
    StructuredBuffer<strivertex> BTriVertex : register(t0);

    ClosestHit() では組み込み関数の PrimitiveIndex() でプリミティブインデックスを取得し StructuredBuffer へのインデックスとして使う。
    疑似コード:

    uint vertId = 3 * PrimitiveIndex();
    float3 hitColor = BTriVertex[vertId + 0].color * barycentrics.x +
                      BTriVertex[vertId + 1].color * barycentrics.y +
                      BTriVertex[vertId + 2].color * barycentrics.z;

以上

詳細

加速構造の構築

keyword

  • 加速構造
    シーン内全てのオブジェクト情報を知っている
  • スクラッチメモリ、リザルトメモリ、インスタンスデスクのサイズ計算
    • それぞれ D3D12_RESOURCE_DESC  が作られる
  • 加速構造 – インタラクティブアップデート
    上述のサイズに影響を与える

    • D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_ALLOW_UPDATE
    • D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_NONE
  • D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS
      // Describe the work being requested, in this case the construction of a
      // (possibly dynamic) top-level hierarchy, with the given instance descriptors
      D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS
      prebuildDesc = {};
      prebuildDesc.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL;
      prebuildDesc.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY;
      prebuildDesc.NumDescs = static_cast<UINT>(m_instances.size());
      prebuildDesc.Flags = m_flags;
    • D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL
    • D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL
    • D3D12_ELEMENTS_LAYOUT_ARRAY
    • D3D12_ELEMENTS_LAYOUT_ARRAY_OF_POINTERS
  • D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO
    • 加速構造の構築に必要なメモリサイズの計算結果を受け取る
  • device->GetRaytracingAccelerationStructurePrebuildInfo(&prebuildDesc, &info)
    • D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO に各種必要メモリサイズの計算結果を出力する
  • D3D12_RAYTRACING_INSTANCE_DESC
  • D3D12_HEAP_PROPERTIES
    ヒープのプロパティを示す。

    // Specifies the default heap. This heap type experiences the most bandwidth for
    // the GPU, but cannot provide CPU access.
    static const D3D12_HEAP_PROPERTIES kDefaultHeapProps = {
        D3D12_HEAP_TYPE_DEFAULT, D3D12_CPU_PAGE_PROPERTY_UNKNOWN, D3D12_MEMORY_POOL_UNKNOWN, 0, 0};

スクラッチメモリ、リザルトバッファ、インスタンスデスク

スクラッチメモリの構築

  • サイズ計算
  • UAV で RESOURCE_DESC を生成する
    • m_device->CreateCommittedResource()
    • RESOURCE_STATES は D3D12_RESOURCE_STATE_UNORDERED_ACCESS

リザルトバッファの構築

  • サイズ計算
  • UAV で RESOURCE_DESC を生成する
    • D3D12_RESOURCE_STATES はD3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE

インスタンスデスク構築

  • サイズ計算
  • RESOURCE_DESC を生成する
  • D3D12_RESOURCE_STATE_GENERIC_READ

インスタンスデスクは ID, Shader Binding Information, 変換行列を保持する。後でここにそいつらをメモリマップしてコピーするのでアップロードバッファじゃないといけない。

インスタンスデスク

typedef struct D3D12_RAYTRACING_INSTANCE_DESC
    {
    FLOAT Transform[ 3 ][ 4 ];
    UINT InstanceID	: 24; // = シェーダから見た時のインスタンス ID
    UINT InstanceMask	: 8;
    UINT InstanceContributionToHitGroupIndex	: 24; // = ヒットグループ ID
    UINT Flags	: 8;
    D3D12_GPU_VIRTUAL_ADDRESS AccelerationStructure;
    } 	D3D12_RAYTRACING_INSTANCE_DESC;

TLAS 生成

At a Glance

  • BLAS/TLAS 共に GPU コマンド BuildRaytracingAccelerationStructure() で Result Buffer に作り込む。
  • BuildRaytracingAccelerationStructure() の入力は BLAS/TLAS 共に D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC がビルダーパラメータになっている。
  • インスタンスデスクは ID, ヒットグループ ID, 変換行列の実態、BLAS のポインタを保持している。これは TLAS 生成のための D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC に仮想アドレスがセットされる。
  • D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC.Type =  D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL を BuildRaytracingAccelerationStructure()  に入力し Result Buffer に TLAS を作り込む。
  • 以上で TLAS が Result Buffer にできる。

Memo

コマンドで生成する。入力はスクラッチメモリ、リザルトバッファ、インスタンスデスクのリソース。

  • D3D12_RAYTRACING_INSTANCE_DESC
    • InstanceID
      visible in the shader in InstanceID()
    • InstanceContributionToHitGroupIndex
      Index of the hit group invoked upon intersection
    • Flags
      including backface culling, winding, etc

      typedef 
      enum D3D12_RAYTRACING_INSTANCE_FLAGS
          {
              D3D12_RAYTRACING_INSTANCE_FLAG_NONE	= 0,
              D3D12_RAYTRACING_INSTANCE_FLAG_TRIANGLE_CULL_DISABLE	= 0x1,
              D3D12_RAYTRACING_INSTANCE_FLAG_TRIANGLE_FRONT_COUNTERCLOCKWISE	= 0x2,
              D3D12_RAYTRACING_INSTANCE_FLAG_FORCE_OPAQUE	= 0x4,
              D3D12_RAYTRACING_INSTANCE_FLAG_FORCE_NON_OPAQUE	= 0x8
          } 	D3D12_RAYTRACING_INSTANCE_FLAGS;
    • Transform
    • InstanceMask
      Visibility mask
    • AccelerationStructure
      BLAS の仮想アドレスをセットする。

      • DONE: Q. どういう構造だったっけ?
        -> ここでセットされているアドレスは ccommandList->BuildRaytracingAccelerationStructure() の書き出し先の Rsult Buffer の仮想アドレス
      • 頂点バッファと頂点数から作られる D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL
          // Create a descriptor of the requested builder work, to generate a
          // bottom-level AS from the input parameters
          D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC buildDesc;
          buildDesc.Inputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL;
          buildDesc.Inputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY;
          buildDesc.Inputs.NumDescs = static_cast<UINT>(m_vertexBuffers.size());
          buildDesc.Inputs.pGeometryDescs = m_vertexBuffers.data();
          buildDesc.DestAccelerationStructureData = {
              resultBuffer->GetGPUVirtualAddress()};
          buildDesc.ScratchAccelerationStructureData = {
              scratchBuffer->GetGPUVirtualAddress()};
          buildDesc.SourceAccelerationStructureData =
              previousResult ? previousResult->GetGPUVirtualAddress() : 0;
          buildDesc.Inputs.Flags = flags;
        
          // Build the AS
          commandList->BuildRaytracingAccelerationStructure(&buildDesc, 0, nullptr);

        結局。ccommandList->BuildRaytracingAccelerationStructure() により Result Buffer と Scrach Memory に何か書き出される。

  • D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC
    Result Buffer に加速構造がコマンドにより作り込まれる。

      // Create a descriptor of the requested builder work, to generate a top-level
      // AS from the input parameters
      D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC buildDesc = {};
      buildDesc.Inputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL;
      buildDesc.Inputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY;
      buildDesc.Inputs.InstanceDescs = descriptorsBuffer->GetGPUVirtualAddress();
      buildDesc.Inputs.NumDescs = instanceCount;
      buildDesc.DestAccelerationStructureData = {resultBuffer->GetGPUVirtualAddress()
                                                 };
      buildDesc.ScratchAccelerationStructureData = {scratchBuffer->GetGPUVirtualAddress()
                                                    };
      // 構築済みの Result Buffer = 加速構造をここにセットし、
      // それを基に加速構造を作れる。
      buildDesc.SourceAccelerationStructureData = pSourceAS;
      buildDesc.Inputs.Flags = flags;
    
      // Build the top-level AS
      commandList->BuildRaytracingAccelerationStructure(&buildDesc, 0, nullptr);
  • 以上

 

Pipeline 構築

TODO: #209 D:\prj\software\Graphics\DirectX\NvDxrTutorialHelloWorld\D3D12HelloTriangle.cpp

シェーダー – RayGen.hlsl

At a glance

  • レイを飛ばすシェーダ
  • 組み込み変数 RayDesc – レイの定義。TraceRay()の引数。Origin, Direction, TMin, TMaxを定義する
  • TMin, TMax – min/max hit distance = レイの開始点と終了点を定義する。
    参考:開始点 = Origin + (Direction * RayTMin) で求まる。
  • 本ステージの組み込み関数 TraceRay(…, inout payload) をコールしレイのトレースが開始される
  • 本ステージの組み込み関数 TraceRay() により起動するシェーダの結果は payload に返る
  • 本ステージの RWTexture2D = Framebuffer に payload の結果を出力して一連の処理が完了

その他

  • 本ステージでよく使われる組み込み関数
  • DispatchRaysIndex() – どのピクセルに対してディスパッチしたかのロケーションを取得する
  • 関数定義直前の [shader(“raygeneration”)] は attribute というものでシェーダの種類を指定する。ここで指定したシェーダの種類に応じて使える組み込み関数が決まる。

参考にしたサイト: