ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
MetalレイトレーシングによるAppの強化
Metalグラフィックスフレームワークとシェーディング言語のコア部分であるレイトレーシングを利用することで、Appやゲームで写真のようにリアルな3Dシーンを実現できます。このセッションでは、レイトレーシングの実装における最新の改善点を探り、プロダクションレンダリングプロセスにおけるアップグレードされた点について確認します。また、より詳細なシーンの作成、ネイティブサポートコンテンツとモーションのインテグレーションなどに役立つMetal APIを紹介します。
リソース
- Accelerating ray tracing using Metal
- Applying realistic material and lighting effects to entities
- Managing groups of resources with argument buffers
- Metal
- Metal Shading Language Specification
- Rendering reflections in real time using ray tracing
関連ビデオ
Tech Talks
WWDC23
WWDC22
WWDC21
-
ダウンロード
♪ (Metalレイトレーシングによる Appの強化) WWDCへようこそ AppleのGPU Compilerエンジニアの Juan Rodriguez Cuellarです このセッションではMetalの レイトレーシングAPIに今年追加した 新機能について紹介します その前に レイトレーシング をおさらいしましょう レイトレーシングAppは 光線がシーンと相互作用する時に たどる線のトレーシングに基づいています レイトレーシングは オーディオや 物理シミュレーション AIなど多分野で応用されていますが 中でも主なものは写実的レンダリングです レンダリングのAppにおいて レイトレーシングは個々の 光線のモデルに使われ それにより反射やソフトシャドウ 間接照明などの効果を シミュレーション できるようになります それがレイトレーシングの 一般的な定義です ではMetalのアプローチについて コンピューティングカーネルから 説明します カーネルで光線を生成し それをシーン内に放射します またそれらの光線と シーンのジオメトリとの インターセクションを インターセクターとアクセラレーション構造で テストします インターセクションポイントは それぞれ表面からバウンスされる光を表し バウンスする光の量と方向で オブジェクトの 見た目を決定します そして各インターセクションの色を計算し 画像を更新します このプロセスはシェーディングと呼ばれ ここでも光線が追加生成される場合があり それに対してもインターセクションの テストが行われます 私たちはシーンの光バウンスを シミュレーションするため このプロセスを必要なだけ 繰り返します 今年は3つの主な分野の新機能に フォーカスしています まずはレンダリングレイトレーシングの レンダリングパイプラインへの サポート追加についてです その後ユーザビリティと ポータビリティに特化した 新機能を紹介します MetalのレイトレーシングAPIを より使いやすくするものです 最後に 今年追加した より写実的なコンテンツ作成を 可能にする プロダクションレンダリング 機能に触れます ではレンダリングパイプラインから のレイトレーシングについてです 1つのレンダーパスを持つ レンダーの基本的な ケースについて考えます レンダリングパイプラインからの レイトレーシング新サポートで レンダーへのレイトレーシング 追加がとても簡単になりました しかしこのサポートがないと 昨年のMetalレイトレーシングAPIで このレンダーにレイトレーシングを 追加するには コンピューティングパスを 追加する必要があります まずはレンダリング後に追加し レンダーされた画像を増強 することから始めましょう この余分なコンピューティングパス を追加するのは レイトレーシングに使う コンピューティングパス用の メモリにさらにアウトプットを 書き出すということです では レンダーパスの間に レイトレーシングを使って ピクセルごとのシャドウなどの 値を計算したい場合は どうでしょうか? レンダリングを分割し コンピューティングパス を導入する必要があります その意味をもっと考えてみると ピクセル位置や法線を レイトレーシングへの インプットとしてメモリに書き出し 恐らく数回にわたり インターセクション結果を 読み返す必要があると いうことになります しかし新しいレンダー段階からの レイトレーシングのサポートでは レンダーパスを残す必要はなく メモリへアウトプットを 書き出すだけです では新APIの使い方を 見てみましょう レイトレーシングをレンダー パイプラインに用意するのは コンピューティングパイプライン に似ています アクセラレーション構造の構築と カスタムインターセクション関数 の定義から始めます カスタムインターセクションの サポートには インターセクションの関数表と それをインターセクション関数で 埋めることが必要です この部分が昨年のAPIとの違いです そのやり方を見ていきましょう 単純なインターセクション関数 について考えます ここには球体や円錐 円環などの オブジェクトを分析的に 交差できる 関数がいくつかあります パイプライン作成時 こうした関数を いわゆるリンク関数として追加します このケースでは パイプラインのフラグメント段階 に追加しています この関数を使うには インターセクション関数表を パイプラインのステートと段階から 作成する必要があります 表ができたら パイプラインのステートと 段階から関数ハンドルを作り 表を埋めることができます フラグメント段階に関数を指定するには 昨年導入した linkedFunctionsオブジェクトを 再利用します レンダーパイプライン記述子には 段階ごとに独自の linkedFunctionsのセットがあります インターセクション関数表の作成は コンピューティングパイプライン とほとんど同じです 段階の増強だけが違いです この表を埋めるには 関数ハンドルを作成します ハンドルは段階に固有のものなので ハンドルリクエスト時に 段階を指定する必要があります 関数ハンドルを得られたら 関数表に挿入するだけです これだけでレンダーパイプラインに 関数表を準備する ことができます これまで構築してきたもの 全てを使って 交差するだけです 実用方法はシンプルです accelerationStructureと インターセクション関数表は両方 レンダーエンコーダでインデックス をバッファします そしてシェーダはこれらの リソースを使って コンピューティング カーネルの時と同じ方法で インターセクターで 光線を交差します レイトレーシングへの パイプラインの準備方法 について詳細は昨年の プレゼンテーションをご覧ください この動画ではアクセラレーション構造の 構築や関数表の作成 そしてシェーディング言語で インターセクターを 使う方法について学びます レンダーパイプラインからの レイトレーシングサポートで 1つのレンダーパス内で レイトレーシングを 追加したり ハイブリッドレンダリングで レイトレーシングを ラスタライゼーションと組み合わせたり Apple Siliconの タイル関数などの 最適化を活用するなど さらなる可能性が開かれています 実際 WWDC 2019の 「Metalによる最新のレンダリング」で デモを行ったサンプルAppに レイトレーシングを 追加していこうと思います レンダーパイプラインからの レイトレーシングでは タイル関数を使って 全てをタイルメモリに 保存するよう コードを更新できます 詳細については今年の 「Metalレイトレーシングによる ハイブリッドレンダリング」をご覧ください 次は今年追加した新機能による MetalレイトレーシングAPI のユーザビリティと ポータビリティの改善についてです これらはMetalレイトレーシングを 使いやすくするだけでなく 他のレイトレーシングAPIからの ポータビリティも提供します その1つがインターセクション クエリです インターセクションクエリでは インターセクションの処理を よりコントロールできます インターセクションクエリは インターセクターにより オーバーヘッドが生じている 単純なユースケース向けです インラインカスタム インターセクションのテストを 実行するオプションを提供する アクセラレーション構造の新しい横断方法です 昨年のインターセクターで カスタムインターセクション を処理する現在の方法を 見てみましょう 昨年のMetalプレゼンでの レイトレーシングの アルファテスト例では アルファテストを使って 沢山の幾何学的詳細を 追加する方法が 説明されていました この鎖と葉の例の通りです また三角インターセクション関数 を使ってインターセクターを カスタマイズすれば アルファテストを簡単に実装できる ことも学びました 三角インターセクション関数 のロジックは 光線がアクセラレーション構造を横断する時に インターセクションを承認するか 拒否するかということです このケースでは1番目の インターセクションを拒否しますが 2番目のインターセクションでは 不透明な表面が 交差されたので これを承認します インターセクション関数の 使われ方を見てみましょう インターセクター使用時に intersect()を呼び出すと アクセラレーション構造を横断して インターセクションを見つけ intersection_resultを埋めます インターセクター内では 潜在的なインターセクションが 見つかるたびに インターセクション関数が呼び出されます そしてインターセクションは インターセクション関数の ロジックに基づいて承認または拒否されます 両方とも高性能かつ便利で インターセクターによる 素晴らしいプログラミングモデルですが 新しいインターセクション関数を作って パイプラインにつなげる必要があります インターセクション関数の ロジックがほんの数行のコードである ケースもあるかもしれません アルファテストロジックでは そうだからです これはアルファテスト実行の ロジックを含む インターセクション関数です インターセクションクエリでは このインターセクション関数を必要とせず このロジックをインラインに配置できます やり方を説明します インターセクションクエリで インターセクション処理を開始する時 光線はアクセラレーション構造を横断し クエリオブジェクトには 横断の状態と結果が 含まれます 光線がカスタムのプリミティブや 不透明ではない三角を 横断する時 インターセクション候補を 評価するためにコントロールが シェーダに返されます 現在の候補がカスタム インターセクションロジックを 横断すると それをコミットして 現在のインターセクションを更新し インターセクション処理を続行します 一方 候補がカスタムインターセクション ロジックに失敗したら 無視して続行することができます インターセクションクエリで アルファテストを行う コードをお見せしましょう まず横断を開始します 次にループで 全ての インターセクション候補を検証します 次に候補のタイプをチェックし 各候補を検証します アルファテストの例では 関心があるのは 三角タイプの インターセクションです タイプをチェックしたら 候補の インターセクション情報をクエリします 現在はインラインのアルファテストの ロジックで必要な情報に 3つのクエリを行います 最後にインターセクション候補が アルファテストに合格したら それをコミットし それが 現在コミットされている インターセクションになります ここまで アクセラレーション構造全体を 横断してインターセクション候補を評価し アルファテストロジックに合格した インターセクションを コミットしてきました ここで コミットされた インターセクションの情報を シェーディングのために クエリする必要があります まずコミットされたタイプをクエリします インターセクション候補のいずれも コミットするインターセクションになる 条件を満たさないと コミットのタイプはnoneで つまり現在の光線が失われます 一方 コミットされたインターセクション がある場合 インターセクションタイプに当てはまる インターセクションについての情報を クエリし これを シェーディングに使います インターセクションクエリで アルファテストを行うのに 必要なコードはこれだけです レンダーパイプラインに インターセクションクエリと インターセクターを 導入したことで AppでMetalレイトレーシング 活用する機会が広がりました インターセクターオブジェクトと インターセクションクエリの どちらを選ぶかで 検討すべき点が いくつかあります まずはコンピューティングで インターセクターを使うなど 既存のコードがあるかどうか そして このコードをポートする 計画についてです 他のAPIからの既存の クエリコードがある場合 インターセクションクエリで このコードをポートできます 次にカスタムインターセクション の複雑な処理があります インターセクターは インターセクション関数と 表を必要とするので インターセクションクエリを使って カスタムインターセクションを自分で 処理した方が簡単かもしれません 最後に検討すべきは パフォーマンスです よりシンプルなケースでは インターセクションクエリで レイトレーシングの パイプライン構築時のオーバーヘッドを 避けることはできますが カスタムインターセクションの処理では 横断中にコードに戻る必要があり ユースケースによっては パフォーマンスに 影響することがあります また複数のクエリオブジェクトを使うと よりメモリが必要になります 一方で インターセクターは インターセクション作業を 全てカプセル化することで より複雑なケースに対応できます 機会があれば 両方のソリューションの パフォーマンスを比較 してみてください インターセクションクエリに ついては以上です では他の新機能について 見ていきましょう これからお話しする2つの新機能は ユーザーインスタンスIDと インスタンス変換です こうした機能により アクセラレーション構造に より多くの情報を追加したり より多くの既存データに アクセスしたりできます これらがなぜ本当に便利な機能かというと 昨年のプレゼンテーションの サンプルコードを振り返ると カーネルボックスのインスタンスが 複数あります この下にはインスタンスの アクセラレーション構造が インスタンスに達するまで分岐する ノードと一緒にあります これら2つのインスタンスは インスタンスの アクセラレーション構造の 最下層レベルにあります 現在 このインスタンスの いずれかを横断すると インターセクション結果から システムのインスタンスID しか得られません これだと自分のデータ表は 維持できますが アクセラレーション構造 に表示できる 便利なデータがあります まずはユーザー定義の インスタンスIDについてです この機能では 各インスタンスの カスタム32ビット値を指定し この値をインターセクションの 結果の一部として取得できます 自分のデータ構造に インデックスできるのは 本当に便利ですが これは同時に カスタムデータの エンコードにも使えます 例えばここではユーザーIDで 各インスタンスのカスタム カラーをエンコードしています これをよりシンプルな反映で 追加の素材情報を調べずに 使えます これはただの一例ですが これによるチャンスは無限大です インスタンスごとの素材IDや インスタンスごとのフラグなど 皆さんがどのように エンコードしたいか想像がつきます AppleではこうしたIDの指定に使う インスタンス記述子バージョンを 拡大しました インスタンスアクセラレーション構造 の記述子で お使いの記述子のタイプを 指定するようにしてください シェーディング言語では 現在のユーザーインスタンスIDの値は インスタンスタグ付きの インターセクション関数への インプットとして利用できます インターセクション後に 値を取得するには インターセクターオブジェクト使用時に インターセクション結果から ユーザー定義の インスタンスIDを得ることができます またインターセクションクエリ オブジェクトを使う時 インターセクション候補と コミットされたインターセクション 両方のユーザー定義 インスタンスIDにアクセスする 対応クエリがあります ユーザーインスタンスID同様 インスタンス変換行列への アクセスにもサポートを追加しました このデータは既にインスタンス 記述子に指定され アクセラレーション構造に 保存されています 今年 こうした行列を シェーディング言語から 表示しました インターセクション関数内の インスタンス変換に インスタンス化と world_space_dataタグ を適用する際にアクセスできます 同様に インスタンス変換は インターセクターを インスタンス化と world_space_dataタグで使う時に インターセクション結果 で提供されます インターセクションクエリを インスタンス化タグで使う時 インターセクション候補と コミットされたインターセクション 両方のインスタンス変換にアクセスする 対応クエリがあります まとめると 今年こうした機能の 導入を通じ MetalレイトレーシングAPI のユーザビリティとポータビリティ が改善されました インターセクションクエリは インターセクターの代替として インターセクション処理に 更なるコントロールを提供します そしてユーザーインスタンスIDと インスタンス変換機能の導入により 外部マッピングをコードで 処理することなく アクセラレーション構造から データにアクセスする能力が実現しました さらにこの3つの機能は 他のレイトレーシングAPI からのポータビリティを提供し クロスプラットフォーム開発を 容易にしてくれます このセッションではここまで 今年追加した レンダーパイプラインの レイトレーシングへの新サポート とポータビリティ機能 についてお話ししました 今度はプロダクションレンダリング 強化を目的に 導入した機能についてです 昨年導入された MetalレイトレーシングAPIによって 素晴らしい高品質コンテンツの レンダリングが実現しています 今年追加した2つの新機能では さらに素晴らしいコンテンツの レンダリングを可能にします まずは制限の延長です MetalレイトレーシングAPI のリリース以来 特にプロダクション規模の ユーザーケースで アクセラレーション構造の内部制限に ぶつかり始めるケースが いくつかありました そのためより大きなシーンに 対応できるよう 制限延長モードへの サポートを追加しました この制限は昨年 典型的なシーンサイズで パフォーマンスを優先できるよう アクセラレーション構造のサイズの バランスを取る目的で設けられました この機能をオンにすると パフォーマンスが 相殺される可能性があり ご自分のAppに最適な モードを選択する必要があります 制限延長モードでは プリミティブやジオメトリ インスタンスの数や インスタンス除去に使われる マスクのサイズの上限が 上げられています これを有効にするには まず アクセラレーション構造の構築時に 制限延長モードを指定します そしてシェーディング言語の インターセクターオブジェクトで extended_limitsタグを指定します これだけで制限延長モード をオンにできます! 次はモーションについてです コンピュータグラフィックでは カメラの露出が瞬時的だと 思われがちですが 一方で実生活では カメラの露出時間は ゼロではありません 露出時間中オブジェクトが カメラに対して動くと 画像の中でぼやけて見えます この極端な例では 真ん中の人物がカメラの露出時間中 ずっと静止している一方 他の人々は動いていて ぼやけています この効果はコンピュータ処理の画像を より現実的に見せるのに とても役立ちます この例では 球体が複数のフレームに またがってアニメ化されていますが 各フレームが瞬時露出されていて アニメーションが途切れています モーションAPIを使うことで ゼロ以外の時間で持続する カメラ露出をシミュレーション できます これでアニメーションが よりスムーズでリアルになります 動画を止めてみると 球体の境界が本物の カメラで撮った時のように 動きの方向にぼやけているのが 見えます ゲームなどリアルタイムのAppでは 画面スペースでこれに近い効果を 使用していますが レイトレーシングでは 物理的に正確なモーションブラーを シミュレーションし シャドウや反射など 非間接的な効果に至るまで 使うことができます では モーションブラーがどのように レンダリングされるか見てみます モーションブラーは レイトレーシングの拡張です ほとんどのレイトレーシングAppは既に 間接照明への入射光の方向など 物理的なディメンションを ランダムにサンプルしています モーションブラーを加えるには 各光線にランダムな時間を選択するだけです Metalはそのシーンをインターセクトし 各光線に関連付けられた 時点と一致させます 例えばこの光線はこのシーンを こんな風に また別の光線はこんな風に 見ています サンプルをさらに多く集めると モーションブラー画像の 要素が集まります カスタムのインターセクション関数を使えば 今すぐにも これを実装できます 全露出中の各プリミティブの 境界ボックスを コンピューティングし これらの境界ボックスを使って アクセラレーション構造を構築できます しかしこれだけでは不十分です 境界ボックスは 巨大になることがあり 光線の中には 実際に接しない プリミティブとの インターセクションを確認する 必要性が発生してしまいます そこでMetal内蔵のモーション ブラーサポートが便利です こうしたケースを効率的に処理する よう設計されています まずMetalシェーディング言語の コードで 各光線に ランダムな時間を 結びつける必要があります 露出間隔内でランダムな時間を 生成するところから始め インターセクターに 渡すだけです 次に必要なのは アニメーションされたジオメトリを Metalに提供することです これはキーフレームアニメーションという 一般的なメソッドを使って行います アニメーションは キーフレームと呼ばれる 時間のキーポイントでボールを モデリングすることで作られます これらキーフレームは アニメーションの 開始と終了間で 均一に分布されます 光線がアクセラレーション構造を 横断する間 いつでも時間値に基づいて キーフレームから データをフェッチすることが できます 例えば光線Aは キーフレーム11で モデルされた通りに シーンを見ます 時間がキーフレーム11に 一致しているからです 対照的に 光線Bの時間は キーフレーム3と4の間です 従って2つのキーフレームの ジオメトリは 光線Bに補間されます モーションはインスタンスと プリミティブレベルの 両方でサポートされています インスタンスアニメーションでは 全オブジェクトの厳格な変換に利用できます これはプリミティブアニメーション より安価ですが オブジェクトの変形が 発生しません 一方で プリミティブアニメーションは より高額ですが スキンされたキャラクターの アニメーションなどに使えます インスタンスもプリミティブ アニメーションも キーフレームアニメーションに 基づくことに留意してください まずはインスタンスモーションに ついてです インスタンスアクセラレーション 構造では 各インスタンスが変換行列と 関連付けられています この行列はシーンのどこに ジオメトリを配置するかを 記述します この例では 2つのプリミティブ アクセラレーション構造があります 1つは球体 もう1つは静的な ジオメトリ用です 各プリミティブアクセラレーション構造には 1つのインスタンスがあり 球体をアニメ化するため アニメーションの開始と 終了時点を示す 2つの変換行列を提供します そしてMetalがこの2つの行列を 各光線の時間パラメータ に基づいて補間します これは2つのキーフレームを使った 特定の例なのですが Metalは任意数のキーフレーム に対応しています こうした行列は アクセラレーション構造記述子を使って 提供されます 標準のMetalインスタンス記述子には 1つの変換行列の余地しかないので 代わりにこの新しいモーション インスタンス記述子を使います この記述子では 変換行列が別々のバッファに 保存されています そして変換バッファ内の インスタンス記述子は 変換バッファ内の変換行列の 範囲を示す開始インデックスと カウントを含みます 各行列は単一のキーフレームを 示しています 新しいモーションインスタンス記述子型で インスタンス記述子を設定する 方法を見ていきましょう まずいつものインスタンス アクセラレーション構造記述子を作ります そして新しいモーションインスタンス 記述子型を使うことを指定し モーションインスタンス記述子を含む instanceDescriptorBufferを 指定します 最後に各キーフレームの 頂点バッファを含む transformBufferを バインドする必要があります 残りのプロパティは 他の インスタンスアクセラレーション構造と同じで 他のアクセラレーション構造と 同様に構築できます シェーディング言語で変更を1つ加えます つまり instance_motionタグを 指定するだけです これはインターセクターに インスタンスモーションの アクセラレーション構造を予測するよう 伝えるものです インスタンスモーションの設定に 必要なのはこれだけです ではプリミティブモーション についてです プリミティブモーションでは 各プリミティブが別々に 動くことができ スキンされたキャラクター のアニメーションなどに使えます 各キーフレームに別々の 3Dモデルを提供が必要だと 覚えておいてください するとMetalがその間に補間します 各キーフレームに頂点データを 提供する必要があります これを例に設定方法を 見てみましょう まず各キーフレームの 頂点バッファを1つの 配列に集めます MTLMotionKeyframeData オブジェクトにより バッファとオフセットを 指定することができます それを使って各キーフレームの 頂点バッファを指定します 次にモーション三角形 ジオメトリ記述子を作成します これは わずかに異なる型を使うことを除き その他のジオメトリ記述子の 作成とよく似ています そして単一の頂点バッファの代わりに vertexBuffersの配列を提供します 最後にいつもの プリミティブアクセラレーション構造 記述子を作り 次にgeometryDescriptor を提供して キーフレーム数を指定します インスタンスモーションと同様 シェーディング言語で 少し変更し primitive_motionタグを 指定する必要があります プリミティブモーションの設定に 必要なのはこれだけです 両方のタイプのアニメーションを 同時に使って よりダイナミックなシーンを 作ることもできます ではこれを実際の アニメーションで見てみましょう これはAdvanced Content チームが作成した パストレーシングレンダリングです この動画はMac Proで AMD Radeon Pro Vega II GPUで レンダリングされました この忍者のキャラクターは 各プリミティブを 別々に動かせるスキンド・ スケルタルアニメーション の技法でアニメ化されました 各フレームはプリミティブ モーションAPIで得られた 256個のランダムにタイムされた サンプルを合わせて作られました スローにすると違いが はっきりと見て取れます 左のバージョンには モーションブラーがなく 右側にはあります また露出時間をさらに増やし 長時間露光を模倣できます リアルさに大きな違いを生む モーションブラーを この新しいモーションAPIで 簡単に追加できます モーションについては ここまでです ご視聴ありがとうございました 皆さんのApp強化に貢献する ツールを提供するため 多くの作業を経て MetalレイトレーシングAPI を作り上げました 皆さんがこれを活用して作成する 素晴らしいコンテンツが とても楽しみです ご視聴ありがとうございました WWDCをお楽しみください ♪
-
-
4:48 - Specify intersection functions on render pipeline state
// Create and attach MTLLinkedFunctions object NSArray <id <MTLFunction>> *functions = @[ sphere, cone, torus ]; MTLLinkedFunctions *linkedFunctions = [MTLLinkedFunctions linkedFunctions]; linkedFunctions.functions = functions; pipelineDescriptor.fragmentLinkedFunctions = linkedFunctions; // Create pipeline id<MTLRenderPipelineState> rayPipeline; rayPipeline = [device newRenderPipelineStateWithDescriptor:pipelineDescriptor error:&error];
-
5:02 - Create intersection function table
// Fill out intersection function table descriptor MTLIntersectionFunctionTableDescriptor *tableDescriptor = [MTLIntersectionFunctionTableDescriptor intersectionFunctionTableDescriptor]; tableDescriptor.functionCount = functions.count; // Create intersection function table id<MTLIntersectionFunctionTable> table; table = [rayPipeline newIntersectionFunctionTableWithDescriptor:tableDescriptor stage:MTLRenderStageFragment];
-
5:14 - Populate intersection function table
id<MTLFunctionHandle> handle; for (NSUInteger i = 0 ; i < functions.count ; i++) { // Get a handle to the linked intersection function in the pipeline state handle = [rayPipeline functionHandleWithFunction:functions[i] stage:MTLRenderStageFragment]; // Insert the function handle into the table [table setFunction:handle atIndex:i]; }
-
5:48 - Bind resources
[renderEncoder setFragmentAccelerationStructure:accelerationStructure atBufferIndex:0]; [renderEncoder setFragmentIntersectionFunctionTable:table atBufferIndex:1];
-
5:57 - Intersect from fragment shader
[[fragment]] float4 rayFragmentShader(vertex_output vo [[stage_in]], primitive_acceleration_structure accelerationStructure, intersection_function_table<triangle_data> functionTable, /* ... */) { // generate ray, create intersector... intersection = intersector.intersect(ray, accelerationStructure, functionTable); // shading... }
-
9:32 - Triangle intersection function
[[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; }
-
10:36 - Custom intersection with intersection query
intersection_query<instancing, triangle_data> iq(ray, as, params); // Step 1: start traversing acceleration structure while (iq.next()) { // Step 2: candidate was found. Check type and run custom intersection. switch (iq.get_candidate_intersection_type()) { case intersection_type::triangle: { bool alphaTestResult = alphaTest(iq.get_candidate_geometry_id(), iq.get_candidate_primitive_id(), iq.get_candidate_triangle_barycentric_coord()); // Step 3: commit candidate or ignore if (alphaTestResult) iq.commit_triangle_intersection() } } }
-
10:39 - Custom intersection with intersection query 2
switch (iq.get_committed_intersection_type()) { // Miss case case intersection_type::none: { missShading(); break; } // Triangle intersection was committed. Query some info and do shading. case intersection_type::triangle: { shadeHitTriangle(iq.get_committed_instance_id(), iq.get_committed_distance(), iq.get_committed_triangle_barycentric_coord()); break; } }
-
15:30 - Specifying user instance IDs
// New instance descriptor type typedef struct { uint32_t userID; // Members from MTLAccelerationStructureInstanceDescriptor... } MTLAccelerationStructureUserIDInstanceDescriptor; // Specify instance descriptor type through acceleration structure descriptor accelDesc.instanceDescriptorType = MTLAccelerationStructureInstanceDescriptorTypeUserID;
-
15:47 - Retrieving user instance IDs 1
// Available in intersection functions [[intersection(bounding_box, instancing)]] IntersectionResult sphereInstanceIntersectionFunction(unsigned int userID[[user_instance_id]], /** other args **/) { // ... }
-
15:58 - Retrieving user instance IDs 2
// Available from intersection result intersection_result<instancing> intersection = instanceIntersector.intersect(/* args */); if (intersection.type != intersection_type::none) instanceIndex = intersection.user_instance_id; // Available from intersection query intersection_query<instancing> iq(/* args */); iq.next() if (iq.get_committed_intersection_type() != intersection_type::none) instanceIndex = iq.get_committed_user_instance_id();
-
16:36 - Instance transforms
// Available in intersection functions [[intersection(bounding_box, instancing, world_space_data)]] IntersectionResult intersectionFunction(float4x3 objToWorld [[object_to_world_transform]], float4x3 worldToObj [[world_to_object_transform]], /** other args **/) { // ... }
-
16:51 - Instance transforms 2
// Available from intersection result intersection_result<instancing, world_space_data> result = intersector.intersect(/* args */); if (result.type != intersection_type::none) { output.myObjectToWorldTransform = result.object_to_world_transform; output.myWorldToObjectTransform = result.world_to_object_transform; }
-
17:03 - Instance transforms 3
// Available from intersection query intersection_query<instancing> iq(/* args */); iq.next() if(iq.get_committed_intersection_type() != intersection_type::none){ output.myObjectToWorldTransform = iq.get_committed_object_to_world_transform(); output.myWorldToObjectTransform = iq.get_committed_world_to_object_transform(); }
-
19:17 - Extended limits
// Specify through acceleration structure descriptor accelDesc.usage = MTLAccelerationStructureUsageExtendedLimits; // Specify intersector tag intersector<extended_limits> extendedIntersector;
-
22:30 - Sampling time
// Randomly sample time float time = random(exposure_start, exposure_end); result = intersector.intersect(ray, acceleration_structure, time);
-
25:54 - Motion instance descriptor
descriptor = [MTLInstanceAccelerationStructureDescriptor new]; descriptor.instanceDescriptorType = MTLAccelerationStructureInstanceDescriptorTypeMotion; // Buffer containing motion instance descriptors descriptor.instanceDescriptorBuffer = instanceBuffer; descriptor.instanceCount = instanceCount; // Buffer containing MTLPackedFloat4x3 transformation matrices descriptor.motionTransformBuffer = transformsBuffer; descriptor.motionTransformCount = transformCount; descriptor.instancedAccelerationStructures = primitiveAccelerationStructures;
-
26:33 - Instance motion
// Specify intersector tag kernel void raytracingKernel(acceleration_structure<instancing, instance_motion> as, /* other args */) { intersector<instancing, instance_motion> intersector; // ... }
-
27:24 - Primitive motion 1
// Collect keyframe vertex buffers NSMutableArray<MTLMotionKeyframeData*> *vertexBuffers = [NSMutableArray new]; for (NSUInteger i = 0 ; i < keyframeBuffers.count ; i++) { MTLMotionKeyframeData *keyframeData = [MTLMotionKeyframeData data]; keyframeData.buffer = keyframeBuffers[i]; [vertexBuffers addObject:keyframeData]; }
-
27:39 - Primitive motion 2
// Create motion geometry descriptor MTLAccelerationStructureMotionTriangleGeometryDescriptor *geometryDescriptor = [MTLAccelerationStructureMotionTriangleGeometryDescriptor descriptor]; geometryDescriptor.vertexBuffers = vertexBuffers; geometryDescriptor.triangleCount = triangleCount;
-
27:57 - Primitive motion 3
// Create acceleration structure descriptor MTLPrimitiveAccelerationStructureDescriptor *primitiveDescriptor = [MTLPrimitiveAccelerationStructureDescriptor descriptor]; primitiveDescriptor.geometryDescriptors = @[ geometryDescriptor ]; primitiveDescriptor.motionKeyframeCount = keyframeCount;
-
28:10 - Primitive motion 4
// Specify intersector tag kernel void raytracingKernel(acceleration_structure<primitive_motion> as, /* other args */) { intersector<primitive_motion> intersector; // ... }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。