ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
Metalを使用したレイトレーシング
レイトレーシング(MetalグラフィックスフレームワークとShading Languageの中核部分)によって、Appやゲームでフォトリアリスティックな3Dシーンを実現します。MetalレイトレーシングAPIおよびレイトレーシングのためのShading Language Extensionの基本を紹介し、それらをグラフィックスAppおよびゲームで使用する方法および最適なパフォーマンスを得るためにカーネルを管理し、シングルコンピューティングカーネルに組み込む方法について学びます。
リソース
- Accelerating ray tracing and motion blur using Metal
- Accelerating ray tracing using Metal
- Debugging the shaders within a draw command or compute dispatch
- Metal
- Metal Feature Set Tables
- Metal Performance Shaders
- Metal Shading Language Specification
- Modern Rendering with Metal
関連ビデオ
WWDC23
WWDC21
-
ダウンロード
こんにちは WWDCへようこそ “Metalを使用した レイトレーシングの検出” GPUソフトウェアエンジニアの ショーン・ジェームスです Metalに今年追加された新しい レイトレーシングAPIについて説明します 新しいAPIでは任意の計算カーネル内から 直接 交差テストを行えます また交差関数を記述して 交差プロセスをカスタマイズ可能です レイトレーシングアプリケーションは 光線の軌跡をたどります 音響や物理シミュレーションなど 多くの分野に適用される中で フォトリアリスティックレンダリングは 人気の1つです レンダリングではレイトレーシングで 個々の光線をモデル化し 反射 ソフトシャドウ 間接照明などの 効果をシミュレートできます まずレイトレーシングについて話し 新しいMetalレイトレーシングAPIをご紹介します
初めにカメラからシーンに放たれる光線を生成 次に光線とシーン内のジオメトリとの 交差をテストします 各交点は表面で跳ね返る光を表し 跳ね返る光の量と方向で オブジェクトの見え方が決まります そして各交点の色を計算し 画像を更新 これはシェーディングと呼ばれ さらに跳ねてシーンに入る 追加の光線も生成できます これらの交差もテストし このプロセスを何度も繰り返して シーン内で跳ね返る光をシミュレートします 過去2年間で Metal Performance Shadersフレームワークの 活用方法をご紹介しました まず光線の初期セットを生成する 計算カーネルを起動し Metalバッファに書き込みます 次にMPSRayIntersectorクラスで 光線をシーンと交差させ インターセクタが結果を 別のバッファに書き込みます そして最終カーネルを起動し 交差の読み取りと シェーディングを行います 追加の光線は 光線バッファに書き戻され 計算カーネル2つをループで起動すると 跳ね返りをシミュレートできます 有効な手法ですが コードを3つの計算カーネルに分けた上 光線と交差をメモリに渡す必要もあります 新しいMetalレイトレーシングAPIでは このインターセクタオブジェクトを シェーディング言語から直接使えます
その結果3つのカーネルを 1つに結合できます カーネル間で光線データを渡さないので 光線バッファと交差バッファも排除できます 光線や交差を渡すための メモリの読み書きも不要です これは柔軟なプログラミングモデルで 計算カーネルを何度も起動せず 外側のループを シェーディング言語の単純なループで表せます では基本的なレイトレーシングカーネルの話です 各ピクセル1スレッドの 2次元計算カーネルを起動します まずrayを生成します これは所定のピクセルの 初期光線を生成する単なる計算です 次にIntersectorオブジェクトを作成 このオブジェクトはシーン内のジオメトリと 光線との交差を検出するもので ここに多様なプロパティを設定し その動作をカスタマイズ可能です 例えばアンビエントオクルージョンや シャドウなどで高速化を図れます 近くの交差を追求せず 最初の交差を受け入れるのです そしてrayをIntersectorに渡し 交差結果を回収 最後に交差結果を使って シェーディングを実行します 交差ステップは多くの作業を伴うので 複雑なシェーディングコードとの結合では 占有率が低い計算カーネルになる 可能性があります カーネル間の最適な分担には アプリケーションの プロファイルが必要です 以上が基本的な交差テストの要件です 交差関数の 別の引数である加速構造は レイトレーシングプロセスを 高速化するためのデータ構造です 空間を再帰的に分割するため 特定の光線と交差し得ない三角形を 素早く削除できます MPSと同様に Metalはこのデータ構造を築きます ユーザーはジオメトリを提供するだけです その仕組みは? 初めに構築したい加速構造を記述する 記述子オブジェクトを作成 次に加速構造を格納するメモリを割り当てて 最後に加速構造を構築します
まずは記述子です Metalはプリミティブ加速構造と インスタンス加速構造をサポートします 今回はプリミティブ加速構造を作成しましょう 三角形などのプリミティブを含むものです プリミティブ加速構造は 個々のジオメトリで構成されます
Metalも2種類のジオメトリを サポートしますが 今回は三角形の例です 三角形ジオメトリの各部には 独自の頂点バッファ インデックスバッファ 三角形の数などが含まれます PrimitiveAccelerationStructureDescriptorを 作成したら 次にgeometryDescriptorを作成します ここでは単一のTriangleGeometryDescriptorです そしてvertexBufferを接続し triangleCountを指定 三角形のデータは 加速構造に埋め込まれるため 加速構造を築いたら vertexBufferの維持は不要です あとはgeometryDescriptorsを accelerationStructureDescriptorに追加する 記述子を作成したので メモリを割り当てられますが これは大きなメモリ割り当てになり得ます 新しいMetalレイトレーシングAPIに 加えた改善の1つは Metalで加速構造が割り当てられる タイミングと場所の完全な制御です つまりアプリケーションの都合で メモリの再利用や割り当てが可能です まずMetal deviceのメソッドを呼び出します これは割り当てに必要なsizesを含む accelerationStructureを返すものです そしてMetal deviceから AccelerationStructureを割り当てて 最後にscratchBufferも割り当てます これは加速構造の構築中に 一時的なストレージとしてMetalに使用され 完了したら破棄できます このバッファ内のデータにはアクセスしないので パフォーマンスを上げるため プライベート リソースストレージモードを使用します 加速構造にメモリを割り当てましたが まだ構築されていません そのため最後に加速構造を構築します
ここでもいくつかの改善を行いました 加速構造の構築は時間を要すことがあるため MetalでどのGPU commandQueueや commandBufferで構築するのかを含め 構築のタイミングを完全に 制御できるようになりました まずcommandBufferを作成し 新しいAccelerationStructure CommandEncodersの1つを作成します 他のMetalエンコーダと同様このオブジェクトは GPU上での作業スケジュールに使用されます この場合 実際のbuildコマンドを エンコードし accelerationStructure scratchBufferを 提供します
最後にエンコーディングを終了し commandBufferをコミットします これでGPUは加速構造の構築を 開始できます 加速構造の構築はCPU同期なしで GPUタイムライン上で 実行されるようになりました このコマンドバッファの後にGPUで 交差作業をスケジュールしても安全です 加速構造の使用法は既に見たように Intersectorに渡すだけです ご覧のように加速構造は通常のMetalバッファ バインディングポイントにバインドします これらのバインディングポイントに 加速構造をバインドするため 対応するメソッドが計算コマンドエンコーダと 引数エンコーダにあります 新しいMetalレイトレーシングAPIの基本的な 構成要素は以上です 今日取り上げなかった 高度なトピックがいくつかあるので ドキュメントとサンプルコードを 確認することをお勧めします 概念の多くは同様なので 前の2つの レイトレーシングに関する講演もご欄ください 例えばMPSのようにMetal加速構造は プリミティブ加速構造の 2レベルのインスタンス化を サポートしています これによりメモリ使用量を減らせます Metalはインスタンス化と組み合わせて 動的ジオメトリに対応可能な 既存の加速構造の再フィットも サポートしています 最後に Metal加速構造は コンパクションもサポートしています コンパクションを使用すると加速構造の構築時 相当量のメモリを回収できます 次に交差関数を使用した交差プロセスの カスタマイズ方法を説明します まずはこれまで取り上げた すべてのデモを見てみましょう
これは高度なコンテンツチームによって 作成されたアプリケーションであり 複雑なシーンに適用される 新しいMetalレイトレーシングAPIのデモです これはMac Proで記録されたシーンの インタラクティブプレビューです 新しいAMD Radeon Pro W5700X GPUを 使用しています インタラクティブプレビューはピクセルごとに 1つのサンプルのみを使用するため ノイズは増えますが カメラの動きが止まると収束し始めます
これは編集中にアーティストが シーンを見る方法です 高品質のオフラインレンダリングの場合 各フレームで十分な時間をとって ノイズのない画像に収束するようにします 最終結果がどのようになるかを把握するために 1ピクセルあたり400サンプルを使用して シーンのフライスルーをレンダリングしました
Megascansライブラリの使用について Quixel社に感謝します こちらはMegascansアセットから組み立てられ 3200万を超える三角形を含んでいます
このアプリケーションは前に説明した アウトラインと非常によく似た働きをします パストレースと呼ばれる レンダリングアルゴリズムを使用して ソフトシャドウ被写界深度間接照明などの 高度な効果をシミュレートします
これでインタラクティブアプリケーションと 完全なオフラインレンダラーの両方で 新しいMetalレイトレーシングAPIを 確認できました 次に新しいMetal APIの最新機能の1つである 交差機能の話に移ります これまでの弊社のプログラミングモデルでは 光線がインターセクタに入り交差が出てきますが インターセクタは常にブラックボックスでした 実際にはインターセクタの動作を カスタマイズしたい場合があります
例えばアルファテストなど ラスタライゼーションの一般的な手法で テクスチャで透明にするべき 三角形の部分をマスクします これにより実際の三角形の数を増やさず 多くの幾何学的詳細を追加できます この例ではアルファテストをオンにすると 葉の細部まで表現できます
これをラスタライゼーションで 実装するのは非常に簡単で フラグメントシェーダーからフラグメントを 破棄するだけです ただしレイトレーシングを使うと 効率的な実装の難易度が上がります 光線をキャストするたび インターセクタは最も近い交差を返します ただしこの場合 交点が実際には透明なので 無視する必要があります
従って最初の交点から始まる 新しい光線をキャストする必要があります 最後に不透明な表面にぶつけて停止し シェーディングに移行できますが 各光線はインターセクタまで 新たに移動する必要があります これではツリー構造のルートから 加速構造トラバーサルを 何度も再開することになるため とても非効率です より効率的に実装するには 三角形の交差関数を使用します
この仕組みを理解するにはインターセクタが 内部でどう機能するか知る必要があります
まず光線とジオメトリ一部の交差が見つかるまで 加速構造をトラバースし 交差が前の最も近い交差よりも 近いかどうかを確認します 近ければその交差を新しい最短の交差にして 加速構造のトラバースに戻ります
最終的に最も近い交差を終了して返します 交差関数はこのプロセスに直接プラグインします これらの関数は交差が 見つかる度に呼び出され 交差機能は交差を受け入れるか 拒否するかを選択できます 受け入れた場合 その交差は 通常どおり新しい最短の交差になります ただし交差の拒否もでき その場合は 加速構造のトラバースに戻り その交差は破棄します アルファテストの例ではアルファテスト ロジックを交差関数に入れることができます その場合加速構造を1回のトラバースでできて はるかに効率的です 基本的な交差関数は次のようになります まず関数を宣言し“triangle”が 交差関数であることを示し “triangle data”は交点の重心座標に アクセスすることを示します この関数は交差を受け入れるか 拒否するかを示すbool値を返します インターセクタからprimitiveIndex geometryIndex交点の 重心座標などの情報を受け取り 使用されない他のデータも一緒に受け取ります アルファテクスチャにアクセスするために使う 独自のリソースをバインドすることもできます まずこのプリミティブの アルファテクスチャを調べます 次に重心座標を使用して テクスチャ座標を補間しますが これはシェーディング手順とまったく同じです 次に実際のテクスチャルックアップを実行し 最後に交点が 透明かどうかに応じてtrueまたは falseを返します ただし交差関数は 三角形のジオメトリに限定されません Metalでは境界ボックスの交差関数を 記述することもできます これらの関数はカスタムプリミティブを モデル化するために使用できる 指定した境界ボックスと 光線が交差する時に呼び出されます このシーンでは球は境界ボックスの 交差機能を使用して実装されています 光線は三角形でモデル化するのではなく 球に対して数学的にテストされます これは非常に単純な例ですが 境界ボックスの交差関数を使用すると 曲線などのより複雑なサーフェスを モデル化することもできます この例では境界ボックスの交差機能を使用して 髪の毛をレンダリングしました 各髪束はカスタムの 3次ベジェ曲線プリミティブであり これを1万個使って 髪に全体的な形を作っています
各曲線プリミティブを交差させると さらに労力を要しますが 三角形の代わりにカーブを使えば アーチファクトのない 完全に滑らかな髪の毛をレンダリングできます また各曲線は多数の三角形の代わりに 少数の制御点しか必要としないため 使用するメモリを減らせます このメモリの効率的な使用は毛量が多い キャラクターの場合に重要です 球プリミティブで作られたシーンを想定します
まず各球を囲む軸に沿った 境界ボックスを提供し 次に三角形の場合と同様に Metalはボックス上で加速構造を構築します
その後 光線を提供するとMetalは 境界ボックスと光線の間の交差を検索します 三角形の場合と同様にMetalでは 潜在的な交差を見つける度に 境界ボックス交差関数を呼び出します この加速構造を構築するには 小さな変更をいくつか加えるだけで済みます
前と同様にPrimitiveAccelerationStructure Descriptorを作成します ただし今回は三角形のジオメトリ記述子を 作成する代わりに BoundingBoxGeometryDescriptorを作成します
この記述子はboundingBoxBufferと はboundingBoxCountの接続場所を提供します
三角形交差関数と同様に 境界ボックス交差関数はtrueを返して 交差の受け入れを示し falseを返して交差の拒否を示します ただしこれらは光線の原点から カスタムプリミティブ上の任意の交点までの 最短距離を計算する役割も果たします Metalはこの距離を使用して シーン内のさまざまな三角形と 境界ボックスプリミティブ全体に渡り 最も近い交差を計算します 境界ボックスの交差関数の記述を示すよう 宣言が少し変更されます 複数の値を返す必要があるため ユーザー定義の構造体を返します この例では交差を受け入れの可否を示す bool値を含む構造体を作成しました 交差距離を含むfloatも含まれます 三角形交差関数と同様に光線のoriginや directionなどの光線データを受け取ります またprimitiveIndexなどの 交差に関する情報も受け取ります さらに独自のリソースもバインドでき このリソースを使用して球の配列を渡します まず境界ボックスで囲まれた球と光線が 交差するかどうかを数学的に確認し 球と光線が交差しない場合 直ちにfalseを返すことができます 次に三角交差関数の場合と異なり 交差関数が許容範囲内にあるかどうかを 確認するもう1つの作業を行います 許容範囲内であれば交差を受け入れ 交差距離を返すことができます しかし追加のデータを返したい場合は どうでしょうか? 例えばシェーディングのために交点で 表面法線を返したい場合には 光線のpayloadを使用します これは交差関数から交差プロセスを開始した 計算カーネルまで遡って渡される 小さなデータです ペイロードタイプへの参照を交差関数の 引数として宣言します 次にそれに書き込みをして ペイロードを変更できます ペイロードへの変更は交差の受け入れに関わらず 表示されることに注意してください そのため多くの場合交差の受け入れ時のみ ペイロードを変更する必要があります ペイロード値を取得するには intersectorへの呼び出しを変更して 参照によってペイロードを渡すだけです intersectorが戻ると交差関数からの ペイロードへの変更はすべて表示されます これまで計算カーネルからの レイインターセクタの使用法と 交差関数の作成方法について説明しました 次にこの2つを組み合わせる 方法について説明します インターセクタが実際に交差関数を 呼び出せるようにするには それらを計算パイプライン状態で リンクする必要があります
まずMetal libraryから InertsectionFunctionをロードしますが これは他のMetal関数と同様に行われます
次にMTLLinkedFunctions オブジェクトを使用して 交差関数をPipelineDescriptorにアタッチします
最後に通常どおりPipelineの状態を コンパイルします コンパイラは計算カーネルと 交差関数のコードをリンクし 交差関数を計算カーネル内の レイインターセクタから呼び出せるようにします これですべての交差関数を 計算パイプライン状態にリンクしたので これらの交差関数を個々のジオメトリに マッピングする必要があります
これには交差関数テーブルと 呼ばれるものを使用します このテーブルにはパイプライン状態の 実行可能コードへのポインターが含まれています 加速構造ジオメトリの各部分が独自の 頂点バッファを持てるのと同様に それぞれが独自の交差関数を持つこともできます またインスタンス加速構造を使用している場合は 各インスタンスに独自の交差関数の セットを含めることもできます AccelerationStructureGeometryと InstanceDescriptorの両方に 個別の intersectionFunctionTableOffsetがあります 光線がジオメトリの一部に当たると これらの2つの値が追加されて 呼び出す交差関数テーブルエントリが決まります 交差関数テーブルを作成するには まず記述子を作成します この例ではすべての交差関数を格納できる 記述子を作成します 次にfunctionTableを割り当てます このテーブルは特定の計算パイプライン状態の 実行可能コードをポイントするため そのパイプライン状態からも作成されます このテーブルはこのパイプライン状態または 派生したパイプライン状態でのみ使用できます 次にcomputePipelineから 各intersectionFunctionのHandleを取得します このHandleはパイプライン状態に リンクされている関数の 実行可能コードのアドレスを表します 次にこのHandleを functionTableに挿入します これはテーブルの交差機能で使われるリソースを バインドする場所でもあります
例えばsphereBufferを Index:0にバインドします 交差関数テーブルの使用法は 加速構造とよく似ています
この場合も交差関数テーブルを intesectorに渡すだけです intersection function tableはMetalバッファ バインディングポイントにもバインドします また交差関数テーブルを 計算コマンドエンコーダと引数エンコーダに バインドする際に対応する APIメソッドもあります 以上が交差関数を設定するために 必要な作業です 交差関数は交差プロセスをより詳細に 制御できる強力なツールです まとめると 最初に新しい MetalレイトレーシングAPIと 任意の計算カーネル内から 直接利用できるようになった レイインターセクタオブジェクトに ついて説明しました これははるかに柔軟な プログラミングモデルであり アプリケーションに最適な方法で 作業を分割できます
また新しいAPIを使用し加速構造を 割り当て構築するタイミングと 場所をより詳細に制御する方法も 説明しました
最後に交差関数を作成して交差プロセスを カスタマイズする方法をお話ししました 交差関数についてお話できることは まだたくさんあります 他のアプリケーションで使用できるよう 基礎となるメカニズムも用意しました
例えば光線はいつでもどの面にも 当たる可能性があるため シェーディングステップでは必要に応じて 任意のマテリアルのコードを 実行できる必要があります このためこのセッションに付随する 関数ポインターの講演をご覧になり これを可能にするもう1つの 強力なツールについて学ぶことをお勧めします 本日は新しいレイトレーシングAPIの 基本を説明したのみでした このセッションに付属するサンプルコードと ドキュメントを確認することもお勧めします 引き続きWWDCをお楽しみください
-
-
2:42 - Ray tracing with Metal
[[kernel]] void rtKernel(primitive_acceleration_structure accelerationStructure [[buffer(0)]], /* ... */) { // Generate ray ray r = generateCameraRay(tid); // Create an intersector intersector<triangle_data> intersector; // Intersect with scene intersection_result<triangle_data> intersection; intersection = intersector.intersect(r, accelerationStructure); // shading... }
-
4:48 - Create an acceleration structure descriptor
let accelerationStructureDescriptor = MTLPrimitiveAccelerationStructureDescriptor() // Create geometry descriptor(s) let geometryDescriptor = MTLAccelerationStructureTriangleGeometryDescriptor() geometryDescriptor.vertexBuffer = vertexBuffer geometryDescriptor.triangleCount = triangleCount accelerationStructureDescriptor.geometryDescriptors = [ geometryDescriptor ]
-
5:46 - Allocate acceleration storage
// Query for acceleration structure sizes let sizes = device.accelerationStructureSizes(descriptor: accelerationStructureDescriptor) // Allocate acceleration structure let accelerationStructure = device.makeAccelerationStructure(size: sizes.accelerationStructureSize)! // Allocate scratch buffer let scratchBuffer = device.makeBuffer(length: sizes.buildScratchBufferSize, options: .storageModePrivate)!
-
6:24 - Build acceleration structure
// Create command buffer/encoder let commandBuffer = commandQueue.makeCommandBuffer()! let commandEncoder = commandBuffer.makeAccelerationStructureCommandEncoder()! // Encode acceleration structure build commandEncoder.build(accelerationStructure: accelerationStructure, descriptor: accelerationStructureDescriptor, scratchBuffer: scratchBuffer, scratchBufferOffset: 0) // Commit command buffer commandEncoder.endEncoding() commandBuffer.commit()
-
7:15 - Pass acceleration structure to ray intersector
[[kernel]] void rtKernel(primitive_acceleration_structure accelerationStructure [[buffer(0)]], /* ... */) { // generate ray, create intersector... intersection = intersector.intersect(r, accelerationStructure); // shading... }
-
7:25 - Bind acceleration structure with compute command encoder
computeEncoder.setAccelerationStructure(accelerationStructure, bufferIndex: 0)
-
12:16 - Triangle intersection functions
[[intersection(triangle, triangle_data)]] bool alphaTestIntersectionFunction(uint primitiveIndex [[primitive_id]], uint geometryIndex [[geometry_id]], float2 barycentricCoords [[barycentric_coord]], device Material *materials [[buffer(0)]]) { texture2d<float> alphaTexture = materials[geometryIndex].alphaTexture; float2 UV = interpolateUVs(materials[geometryIndex].UVs, primitiveIndex, barycentricCoords); float alpha = alphaTexture.sample(sampler, UV).x; return alpha > 0.5f; }
-
14:38 - Creating a bounding box acceleration structure
// Create a primitive acceleration structure descriptor let accelerationStructureDescriptor = MTLPrimitiveAccelerationStructureDescriptor() // Create one or more bounding box geometry descriptors: let geometryDescriptor = MTLAccelerationStructureBoundingBoxGeometryDescriptor() geometryDescriptor.boundingBoxBuffer = boundingBoxBuffer geometryDescriptor.boundingBoxCount = boundingBoxCount accelerationStructureDescriptor.geometryDescriptors = [ geometryDescriptor ]
-
15:29 - Bounding Box Result
struct BoundingBoxResult { bool accept [[accept_intersection]]; float distance [[distance]]; };
-
15:38 - Bounding box intersection functions
[[intersection(bounding_box)]] BoundingBoxResult sphereIntersectionFunction(float3 origin [[origin]], float3 direction [[direction]], float minDistance [[min_distance]], float maxDistance [[max_distance]], uint primitiveIndex [[primitive_id]], device Sphere *spheres [[buffer(0)]]) { float distance; if (!intersectRaySphere(origin, direction, spheres[primitiveIndex], &distance)) return { false, 0.0f }; if (distance < minDistance || distance > maxDistance) return { false, 0.0f }; return { true, distance }; }
-
16:20 - Ray payload
[[intersection(bounding_box)]] BoundingBoxResult sphereIntersectionFunction(/* ... */, ray_data float3 & normal [[payload]]) { // ... if (distance < minDistance || distance > maxDistance) return { false, 0.0f }; float3 intersectionPoint = origin + direction * distance; normal = normalize(intersectionPoint - spheres[primitiveIndex].origin); return { true, distance }; }
-
16:48 - Ray payload 2
[[kernel]] void rtKernel(/* ... */) { // generate ray, create intersector... float3 normal; intersection = intersector.intersect(r, accelerationStructure, functionTable, normal); // shading... }
-
17:18 - Linking intersection functions
// Load functions from Metal library let sphereIntersectionFunction = library.makeFunction(name: “sphereIntersectionFunction”)! // other functions... // Attach functions to ray tracing compute pipeline descriptor let linkedFunctions = MTLLinkedFunctions() linkedFunctions.functions = [ sphereIntersectionFunction, alphaTestFunction, ... ] computePipelineDescriptor.linkedFunctions = linkedFunctions // Compile and link ray tracing compute pipeline let computePipeline = try device.makeComputePipeline(descriptor: computePipelineDescriptor, options: [], reflection: nil)
-
18:17 - Intersection function table offsets
class MTLAccelerationStructureGeometryDescriptor : NSObject { var intersectionFunctionTableOffset: Int // ... } struct MTLAccelerationStructureInstanceDescriptor { var intersectionFunctionTableOffset: UInt32 // ... };
-
18:35 - Creating an intersection function table
// Allocate intersection function table let descriptor = MTLIntersectionFunctionTableDescriptor() descriptor.functionCount = intersectionFunctions.count let functionTable = computePipeline.makeIntersectionFunctionTable(descriptor: descriptor) for i in 0 ..< intersectionFunctions.count { // Get a handle to the linked intersection function in the pipeline state let functionHandle = computePipeline.functionHandle(function: intersectionFunctions[i]) // Insert the function handle into the table functionTable.setFunction(functionHandle, index: i) } // Bind intersection function resources functionTable.setBuffer(sphereBuffer, offset: 0, index: 0)
-
19:26 - Pass intersection function table to ray intersector
[[kernel]] void rtKernel(primitive_acceleration_structure accelerationStructure [[buffer(0)]], intersection_function_table<triangle_data> functionTable [[buffer(1)]], /* ... */) { // generate ray, create intersector... intersection = intersector.intersect(r, accelerationStructure, functionTable); // shading... }
-
19:33 - Bind intersection function table with compute command encoder
encoder.setIntersectionFunctionTable(functionTable, bufferIndex: 1)
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。