View in English

  • メニューを開く メニューを閉じる
  • Apple Developer
検索
検索を終了
  • Apple Developer
  • ニュース
  • 見つける
  • デザイン
  • 開発
  • 配信
  • サポート
  • アカウント
次の内容に検索結果を絞り込む

クイックリンク

5 クイックリンク

ビデオ

メニューを開く メニューを閉じる
  • コレクション
  • トピック
  • すべてのビデオ
  • 利用方法

その他のビデオ

  • 概要
  • トランスクリプト
  • コード
  • イマーシブなアプリを作成するためのMetalレンダリングの新機能

    Compositor Servicesによりアプリのイマーシブ体験を向上させるための、Metalのレンダリングにおける最新の機能強化について確認します。アプリのインタラクティブな要素を強調表示するためにホバーエフェクトを追加する方法や、動的なレンダリング品質を使用してより高画質なレンダリングを行う方法を紹介するほか、新しい段階的イマーシブスタイルについて説明します。また、MetalコンテンツをMacからVison Proに直接レンダリングすることで、macOSアプリでイマーシブ体験を実現する方法を解説します。 このセッションの内容を十分理解できるよう、まずWWDC23の「Discover Metal for immersive apps」を視聴することをおすすめします。

    関連する章

    • 0:00 - イントロダクション
    • 1:58 - レンダリングループのための新しいAPI
    • 4:21 - ホバーエフェクト
    • 10:50 - 動的レンダリングの品質
    • 14:44 - 段階的なイマーシブスタイル
    • 18:32 - macOSの空間レンダリング
    • 23:51 - 次のステップ

    リソース

    • Analyzing the performance of your Metal app
    • Optimizing GPU performance
    • Rendering hover effects in Metal immersive apps
      • HDビデオ
      • SDビデオ

    関連ビデオ

    WWDC25

    • Metal 4の概要
    • Metal 4ゲームの知識を深める
    • Metal 4ゲームの詳細
    • SwiftUIの新機能
    • visionOS 26の新機能
    • visionOSでの空間アクセサリ入力の詳細

    WWDC24

    • visionOSでのパススルーによるMetalのレンダリング

    WWDC23

    • イマーシブアプリのためのMetal
  • このビデオを検索

    こんにちは Ricardoです Appleのソフトウェアエンジニアです このセッションでは Apple Vision Proで Metalを使用して イマーシブコンテンツを描画するための 新機能をご紹介します 昨年説明したのは Metal Compositor Services、ARKitを活用し visionOSでコンテンツを直接描画して イマーシブ体験を生み出す方法でした

    この方法ではフルイマーシブ体験も 実装できますが その例が Resolution Gamesの「Demeo」などです またはmixedイマーシブスタイルを使い 実世界と重ねて コンテンツを表示することもできます

    そして 皆さんのようなデベロッパからの 有益なフィードバックを反映して visionOSのMetalレンダリングに 注目すべき新機能を追加しました

    アプリやゲームに 豊かなディテールと インタラクティブなエフェクトを 追加できる機能です このビデオでは この新機能 Compositor Servicesの詳細を説明します

    この機能を最大限に活用するために 必要となるのが Compositor Servicesフレームワークと Metalレンダリング手法の知識です これらの技術を使用したことがない場合は 画面下部に示している 過去のビデオで基礎を学ぶことができます

    この新機能を導入するには まず既存のレンダリングループに 変更を加える必要があります 最初に パイプラインを柔軟にする 新しいAPIを導入する方法を説明します それを受けて ホバーエフェクトを追加し アプリのインタラクティブな要素を ハイライトする方法を紹介します レンダリングされたコンテンツの解像度を 動的に調整する方法も説明します 新しい段階的なイマーシブスタイルでは Digital Crownを使用して イマーシブのレベルを調整できます Macを使用して イマーシブコンテンツを Vision Proに直接描画する方法も紹介します すべての開始点となるのが 新しいレンダリングループAPIです その説明から始めましょう

    Metalイマーシブアプリの構築は コンポジタレイヤーを持つイマーシブ空間を SwiftUIで作成することから始まります

    このレイヤーは レンダリングループで使う レイヤーレンダラオブジェクトを提供します レイヤーレンダラからフレームをクエリし 各フレームから 描画可能オブジェクトを取得しますが これには コンテンツのレンダリングに使う テクスチャが含まれます Metalイマーシブアプリを すでに作成している場合は レンダリングされたフレームごとに1つずつ 描画可能オブジェクトをクエリしています 今年 Compositor Servicesには 描画可能オブジェクトの配列を返す 新しいqueryDrawables関数が追加されました システムコンテキストに応じて 配列には1つか 2つの描画可能オブジェクトが含まれます ほとんどの場合 描画可能オブジェクトは1つですが Reality Composer Proで 高品質のビデオを 録画する場合は 描画可能オブジェクトは2つ返されます 描画可能オブジェクトの識別には 新しいtargetプロパティを使用できます Vision Proディスプレイ用のものは .builtIn値を持ち 録画用のものは .capture値を持ちます 高品質ビデオのキャプチャ方法については デベロッパ向けドキュメントをご覧ください

    このrenderFrame関数は 皆さんにはおなじみかもしれません 次のフレームをクエリし シーンの状態を更新した後 queryDrawable関数を呼び出します 最適な入力時間を待機して シーンを描画可能オブジェクトに レンダリングします

    今度はqueryDrawableを queryDrawablesに置き換えます 配列が空でないことを確認し シーンを すべての描画可能オブジェクトに描画します

    Metalイマーシブアプリの作成方法を示す 便利なテンプレートがXcodeにはあります これは優れた出発点です 確認するには visionOSアプリ用の 新しいXcodeプロジェクトを開始し で を選択します

    なお Metal 3は今年も 従来と同様にフルサポートされており Xcodeテンプレートでオプションを 選択することで使用できます

    最新バージョンのMetalの導入方法については 「Discover Metal 4」をご覧ください 新しいqueryDrawables関数を導入すると 今年の新機能すべてが使用可能になります シーンのインタラクティブオブジェクトへの ホバーエフェクトの追加などです

    この機能を使うと アプリユーザーは どのオブジェクトがインタラクティブか 簡単にわかり アクションの対象を予測できます ユーザーが見ているオブジェクトを システムが動的にハイライトします 例えば パズルゲームでプレーヤーが 選択可能なピースを強調表示できます

    複数の3Dオブジェクトを含むシーンを レンダリングするアプリで 一部のオブジェクトしか 操作できないというケースで シーンのインタラクティブオブジェクトのみ ホバーエフェクトを適用したいとします オブジェクトがインタラクティブでない場合 トラッキングされず ホバーエフェクトも適用されません 通常通りレンダリングします これに対し オブジェクトが インタラクティブな場合 トラッキング対象の 新しい領域を 描画可能オブジェクトに追加します 登録した各トラッキング領域に 一意のオブジェクト識別子を 割り当てる必要があります

    次に オブジェクトに ホバーエフェクトを適用すべきか確認します 不要な場合は トラッキング領域の レンダリング値で描画します これは オブジェクトのピンチを 検出するのに便利です ホバーエフェクトを オブジェクトに適用すべき場合は レンダリング前にトラッキング領域で エフェクトを設定します

    コードでは ホバーエフェクトを使うように レイヤーレンダラを設定する必要があります 8ビットピクセル形式を使うように トラッキング領域のテクスチャを設定します インタラクティブオブジェクトを 同時に最大255個サポートします フォーマットがレイヤー機能のサポートの 対象であることを確認し 構成で設定します

    トラッキング領域の登録コードで オブジェクトがインタラクティブか確認し インタラクティブであれば オブジェクト識別子を使用して 描画可能オブジェクトに 新しいトラッキング領域を登録します 必ず オブジェクトのライフサイクルを通じ 一意の識別子をトラッキングしてください 次に オブジェクトに ホバーエフェクトを適用するか確認し 適用している場合は .automatic属性を使用して追加します これにより ユーザーが オブジェクトを見ると自動的に ホバーエフェクトが追加されます 最後に レンダリングします

    Compositor Servicesでは コンテンツの レンダリングのための複数のテクスチャを 描画可能オブジェクトが提供します

    ユーザーに表示される色を示す 色テクスチャは 皆さんおなじみでしょう 深度テクスチャもあり 暗い色は ユーザーから遠いオブジェクトを示します システムはこのテクスチャを使い ユーザーのシーン内での移動に合わせて コンテンツをより正確に表示します 今年は シーン内の 様々なインタラクティブ領域を定義する トラッキング領域テクスチャも 追加されました 描画可能オブジェクトは 色と深度のテクスチャを提供します これで 新しいトラッキング領域の テクスチャもクエリできます インタラクティブオブジェクトに対応する 個別の領域を そのテクスチャで描画します ホバーエフェクトでは ユーザーがシーンの インタラクティブオブジェクトを見た時に システムはトラッキング領域テクスチャを 使用して 対応する領域を見つけ 色テクスチャの一致する部分に ホバーエフェクトを適用します ここでも 設定したトラッキング領域を使う オブジェクトレンダリング関数があります トラッキング領域テクスチャのレンダリング には システムが計算するレンダリング値が 必要なので その値を格納するローカル変数を宣言し 対応するトラッキング領域から その変数を取得します オブジェクトがインタラクティブでない場合 既定のレンダリング値nilを使用できます 最後に それをdraw関数に渡して フラグメントシェーダに送信します

    シェーダのコードを見てみましょう

    フラグメントシェーダの出力には トラッキング領域のレンダリング値があり インデックス1の 色アタッチメントにマップされています そこにトラッキング領域テクスチャを 設定しました レンダリング値を uniforms構造体にバインドし シェーダ出力で色の値とともに返します これで インタラクティブな ホバーエフェクトがアプリに追加されました マルチサンプルのアンチエイリアシング (MSAA)を使う場合は注意点があります

    この手法では 中間の 高解像度テクスチャをレンダリングしてから サンプリング期間の全体で 色の値を平均化します これを行うには通常 ターゲットテクスチャ格納アクションで multisampleResolveオプションを使います 色ピクセルを解決するのと同じ方法で トラッキング領域テクスチャを 解決することはできません その方法では レンダリング値が平均化されて どのトラッキング領域にも対応しない 無効なスカラ値が生成されます multisampleResolveを 色に使用する場合 トラッキング領域テクスチャ用にカスタムの タイルリゾルバを実装する必要があります これを行うには カスタムの タイルレンダリングパイプラインで dontCare格納オプションを使います おすすめの方法は MSAAソーステクスチャの サンプリング期間において最も頻繁に表れる レンダリング値を選択することです MSAAでの使用方法など ホバーエフェクトの詳細については デベロッパ向けドキュメントの「Rendering hover effects in Metal immersive apps」を ご覧ください

    トラッキング領域を使用すると オブジェクトのインタラクションの処理も 以前よりも簡単になります 空間イベントでは nullを許容する トラッキング領域識別子を使用できます この識別子は シーンオブジェクトと 領域の一致の確認に使用できます 対象のオブジェクトが見つかったら それに対しアクションを実行できます

    ホバーエフェクトを使うことで アプリが よりインタラクティブになりました 操作可能なオブジェクトや ピンチするとアクティブになるオブジェクトを アプリユーザーは明確に把握できます これで 入力イベントの処理も これまで以上に簡単になります 今年の進化により 以前よりも高い忠実度で コンテンツを描画できるようになりました 動的レンダリングの品質を使用すると シーンの複雑さに応じて コンテンツの解像度を調整できます

    まず中心窩レンダリングの仕組みを 簡単に説明します 標準の非中心窩のテクスチャでは ピクセルは表面全体に 均等に分散されます 中心窩レンダリングでは 中心のピクセル密度が高いテクスチャを 描画できるようにシステムが支援します これにより アプリで 計算リソースと電力リソースを使用して ユーザーが見る可能性が高い場所で コンテンツの見栄えを 良くすることができます 今年の改善により 中心窩レンダリングで 動的な品質を活用できるようになりました アプリによってレンダリングされる フレームの品質を制御できます まず アプリに適した最大の レンダリング品質を指定する必要があります これは アプリのレンダリングセッションの 上限の設定です 次に 実行時の品質の許容範囲を 表示するコンテンツの種類に応じて 設定できます

    レンダリング品質を上げると テクスチャの関連性の高い領域が拡大し テクスチャ全体のサイズが大きくなります 念のためですが 品質を上げると アプリでのメモリと電力の 使用量も増加します テキストやユーザーインターフェイス要素を レンダリングする場合は レンダリング品質を高く設定すると有効です ただし 複雑な3Dシーンを表示する場合は 計算リソースの制約を受けることがあります アプリをスムーズに動作させるには 高品質なビジュアルと アプリの消費電力を バランスよく考慮する必要があります

    Instrumentsを使うと アプリの パフォーマンスをリアルタイムで分析できます また Metalデバッガを使用すれば Metalコードとシェーダを 詳しく調査して最適化できます 重要なのは 最も複雑なシーンはどこか アプリをプロファイリングして フレームを安定したペースで描画するために 十分な時間を確保することです

    デベロッパ向けドキュメントで Metalレンダリングアプリの最適化について ご確認いただけます

    このコードサンプルでは アプリをプロファイリングし アプリのメニューを 0.8の品質で レンダリングすることを判断しました これなら テキストがより鮮明に見えます 世界は 複雑なシーンなので 0.6の品質でレンダリングします ‭また 計算プロパティを追加して 使用する最大レンダリング品質を 指定しています これが今回のレイヤー設定です 動的レンダリングの品質は 中心窩でしか使用できないので 有効になっているか確認します 次に最大レンダリング品質を 計算プロパティの値に設定します コンテンツに適した最小値に 設定するよう注意してください そうしないと アプリは 必要以上のメモリを消費します

    新しいシーンをロードする時に adjustRenderQuality関数を呼び出します レンダリング品質の調整は 中心窩が有効な場合のみ可能です シーンタイプを切り替えて それに応じてレンダリング品質を調整します

    品質の値の遷移は瞬時には完了せず 少し時間がかかります システムによって遷移がスムーズになります

    動的レンダリングの品質により 非常に詳細なシーンの魅力が高まります レンダリングされるシーンの解像度を 高くすると 細かいディテールが よりクリアに表示されます ただし 非常に複雑なシーンでは 品質を下げる必要があるかもしれません これで アプリのコンテンツの レンダリング品質を調整できるようになりました

    今年の新機能として 段階的なイマーシブのポータル内で Metalアプリを レンダリングできるようになりました 段階的なイマーシブスタイルでは アプリのユーザーは Digital Crownを回すことで イマーシブのレベルを制御できます このモードでは ユーザーは 動きのある複雑なシーンを観る時も 実世界の環境を感じられるので 安心感を得ることができます 段階的なイマーシブモードで Metalアプリを表示している場合 現在のイマーシブレベルに含まれる コンテンツのみがレンダリングされます

    これは フルイマーシブで レンダリングされたゲームシーンの例です そしてこれは ユーザーがDigital Crownを調整して 部分的なイマーシブで レンダリングされた同じシーンです 2つのシーンを比較してわかるのは ポータル外の領域をレンダリングしないため 計算能力を節約できるということです ポータル外の部分は見えないので レンダリングする必要はありません 新しいAPIでは システムが計算するポータルステンシルを 使って コンテンツをマスクできます この白い楕円は 対応するポータルステンシルを示しています ステンシルのバッファは レンダリングされた シーンのマスクとして機能します これにより ポータル内の コンテンツのみがレンダリングされます ご覧の通り レンダリングされている シーンは エッジが滑らかではありません コマンドバッファの最終ステップとして フェードがシステムによって適用され ユーザーに表示されるシーンが作成されます

    ステンシルを使って 不要なコンテンツが レンダリングされないようにするには まずコンポジタレイヤーを設定します レイヤー機能が 適用するステンシルの フォーマットをサポートしていることを 確認し 構成で設定します ポータルステンシルマスクを適用するには コマンドバッファで描画可能オブジェクトに レンダリングコンテキストを追加します ステンシルにマスクを描画すると 表示されないピクセルが レンダリングされなくなります また レンダリングコンテキストを介して エンコードを終了する必要があります コマンドエンコーダを 直接終了するのではありません これにより ポータルエフェクトが コンテンツに効率的に適用されます このアプリでは SwiftUIでイマーシブ空間を作成し 段階的なイマーシブスタイルを 新しいオプションとしてリストに追加します アプリユーザーは段階的とフルの スタイルを切り替えられます 次にレイヤーを設定します まず 注意点として 段階的なイマーシブスタイルは レイヤーレイアウトでしか機能しません 目的のステンシル形式を ピクセルあたり8ビットに指定します このフォーマットがサポートされていることを 確認し 構成で設定します また MSAAは使用していないので サンプル数を1に設定します MSAAを使用する場合は MSAAのサンプル数に設定します

    レンダラで 描画可能オブジェクトに レンダリングコンテキストを追加します レンダリングコマンドに使用するのと同じ コマンドバッファを渡します 次に ポータルマスクを ステンシルアタッチメントに描画します 他のステンシル操作では使用しない ステンシル値を選択しました ステンシルの参照値を レンダリングエンコーダで設定します レンダラは現在のイマーシブレベルの 外の領域を描画しなくなります シーンをレンダリングした後 描画可能オブジェクトの レンダリングコンテキストでエンコードを終了します

    段階的なイマーシブスタイルを使った レンダラの実際の例を確認するには visionOSのMetalアプリテンプレートで オプションを選択します これで ポータルスタイルの Metalアプリの構築を開始できます

    最後に取り上げるのは macOSの空間レンダリングです

    ここまでは Vision Proでのネイティブな イマーシブ体験の構築について見てきました 今年 Macのパワーを活用して イマーシブコンテンツをVision Proに 直接レンダリングし ストリーミングできるようになりました これにより 既存のMacアプリに イマーシブ体験を追加できます

    例えば 3Dモデリングアプリから シーンを直接 Vision Proでプレビューできます また イマーシブなmacOSアプリを ゼロから構築することもできます これにより Vision Proの 電力消費に制約されることなく 高い計算能力を必要とする 複雑なイマーシブ体験を実現できます リモートイマーシブセッションを Macアプリから開始するのはとても簡単です macOSでイマーシブ空間を開くと Vision Proで接続を 受け入れるように求められます

    受け入れると Macでレンダリングされた イマーシブコンテンツが表示されます

    一般的なMacアプリは SwiftUIまたはAppKitで構築されます これらのフレームワークのいずれかを使い ウインドウを作成して表示します ウインドウのコンテンツは Core Animationでレンダリングされます 様々なmacOSフレームワークを採用して アプリの機能を実装できます そして コンテンツは Macのディスプレイに表示されます Macを活用した イマーシブ体験を構築する際は イマーシブなvisionOSアプリを作成できる 使い慣れたフレームワークを使用します まずSwiftUIと 新しいシーンタイプの RemoteImmersiveSpaceを使用します 次に Compositor Services フレームワークを導入します ARKitとMetalを使用して コンテンツを配置しレンダリングします すると イマーシブシーンが Vision Proに直接表示されます macOSのRemoteImmersiveSpaceは ネイティブのvisionOSアプリと同様に コンポジタレイヤーと ARKitセッションをホストします これらは Vision Proのディスプレイや センサーにシームレスに接続されます ARKitセッションを visionOSに接続するための remoteDeviceIdentifierという 新しいSwiftUI環境オブジェクトがあります このオブジェクトを セッションイニシャライザに渡します これが Macイマーシブアプリの構造です

    コンポジタコンテンツを含む 新しいリモートイマーシブ空間を定義します この空間で コンポジタレイヤーが どのように使われるかは後ほど説明します Macでサポートされるイマーシブスタイルは 段階的とフルのみです Macアプリのインターフェイスで supportsRemoteScenesという 新しい環境変数を使用して Macにこの機能があるか確認します リモートシーンがサポートされていない場合 メッセージを表示するようUIを設定できます サポートされていて まだイマーシブ空間を開いていない場合は 起動できます このアプリの最後の部分は コンポジタコンテンツです コンポジタレイヤーと ARKitセッションがあります visionOSの場合と同じ方法で コンポジタレイヤーを作成して使用します 新しいSwiftUI環境オブジェクトの remoteDeviceIdentifierにアクセスし ARKitセッションの イニシャライザに渡します これで MacのARKitセッションが Vision Proに接続されます 最後に 一般的なMetalイマーシブアプリと 同じようにレンダリングループを開始します

    ARKitとワールドトラッキングプロバイダが macOSで利用可能になりました これにより 空間内での Vision Proの位置をクエリできます ネイティブイマーシブアプリの場合と 同様に レンダリング前に デバイスのポーズを使って シーンと描画可能オブジェクトを更新します macOSの空間アプリは Macに接続された すべての入力デバイスをサポートします キーボードとマウスの コントロールを使用できます また ゲームパッドを接続し Game Controllerフレームワークを使い 入力を処理することもできます さらに レイヤーレンダラの onSpatialEventモディファイアを使用して イマーシブシーンのインタラクティブ要素で ピンチイベントを使えます

    また今年 既存のAppKitやUIKitアプリからも SwiftUIシーンを作成可能になりました これは 新しいイマーシブ体験を既存の Macアプリに追加するための優れた方法です この方法の詳細については 「What's new in SwiftUI」をご覧ください

    レンダリングエンジンは CまたはC++で実装されるのが一般的です 説明したすべてのAPIには Cのネイティブな同等機能があります Compositor Servicesフレームワークの Cデータ型は接頭辞cpで始まります これらの型では Core Foundationなど よく利用されるCライブラリと同様の パターンと規則を使用します ARKitの場合 cDeviceプロパティは C互換の remoteDeviceIdentifierを提供します これをCフレームワークに渡し create_with_device関数で ARKitセッションを初期化できます これらすべての要素により Macを使用して Vision Proで表示する イマーシブコンテンツを強化できます

    これらの新機能を 皆さんがどのように活用して イマーシブアプリを拡張するか楽しみです これらの機能により アプリは よりインタラクティブかつ忠実度が高くなり 新しい段階的な イマーシブスタイルも利用できます 新しいmacOSの空間機能で 皆さんが実現するアプリに期待しています イマーシブアプリを次のレベルに 引き上げる方法の詳細については 「Set the scene with SwiftUI in visionOS」をご覧ください その他のプラットフォームの 機能強化の概要については 「What's new in visionOS」をご覧ください ご視聴ありがとうございました

    • 0:01 - Scene render loop

      // Scene render loop
      
      extension Renderer {
          func renderFrame(with scene: MyScene) {
              guard let frame = layerRenderer.queryNextFrame() else { return }
      
              frame.startUpdate()
              scene.performFrameIndependentUpdates()
              frame.endUpdate()
      
              let drawables = frame.queryDrawables()
              guard !drawables.isEmpty else { return }
      
              guard let timing = frame.predictTiming() else { return }
              LayerRenderer.Clock().wait(until: timing.optimalInputTime)
              frame.startSubmission()
              scene.render(to: drawable)
              frame.endSubmission()
          }
      }
    • 5:54 - Layer configuration

      // Layer configuration
      
      struct MyConfiguration: CompositorLayerConfiguration {
          func makeConfiguration(capabilities: LayerRenderer.Capabilities,
                                 configuration: inout LayerRenderer.Configuration) {
              // Configure other aspects of LayerRenderer
      
              let trackingAreasFormat: MTLPixelFormat = .r8Uint
              if capabilities.supportedTrackingAreasFormats.contains(trackingAreasFormat) {
                  configuration.trackingAreasFormat = trackingAreasFormat
              }
          }
      }
    • 7:54 - Object render function

      // Object render function
      
      extension MyObject {
          func render(drawable: Drawable, renderEncoder: MTLRenderCommandEncoder) {
              var renderValue: LayerRenderer.Drawable.TrackingArea.RenderValue? = nil
              if self.isInteractive {
                  let trackingArea = drawable.addTrackingArea(identifier: self.identifier)
                  if self.usesHoverEffect {
                      trackingArea.addHoverEffect(.automatic)
                  }
                  renderValue = trackingArea.renderValue
              }
      		self.draw(with: commandEncoder, trackingAreaRenderValue: renderValue)
          }
      }
    • 8:26 - Metal fragment shader

      // Metal fragment shader
      
      struct FragmentOut
      {
          float4 color [[color(0)]];
          uint16_t trackingAreaRenderValue [[color(1)]];
      };
      
      fragment FragmentOut fragmentShader( /* ... */ )
      {
          // ...
      
          return FragmentOut {
              float4(outColor, 1.0),
              uniforms.trackingAreaRenderValue
          };
      }
    • 10:09 - Event processing

      // Event processing
      
      extension Renderer {
          func processEvent(_ event: SpatialEventCollection.Event) {
             let object = scene.objects.first {
                 $0.identifier == event.trackingAreaIdentifier
             }
             if let object {
                 object.performAction()
             }
         }
      }
    • 13:08 - Quality constants

      // Quality constants
      
      extension MyScene {
          struct Constants {
              static let menuRenderQuality: LayerRenderer.RenderQuality = .init(0.8)
              static let worldRenderQuality: LayerRenderer.RenderQuality = .init(0.6)
              static var maxRenderQuality: LayerRenderer.RenderQuality { menuRenderQuality }
          }
      }
    • 13:32 - Layer configuration

      // Layer configuration
      
      struct MyConfiguration: CompositorLayerConfiguration {
          func makeConfiguration(capabilities: LayerRenderer.Capabilities,
                                 configuration: inout LayerRenderer.Configuration) {
             // Configure other aspects of LayerRenderer
      
             if configuration.isFoveationEnabled {
                 configuration.maxRenderQuality = MyScene.Constants.maxRenderQuality
             }
      }
    • 13:57 - Set runtime render quality

      // Set runtime render quality
      
      extension MyScene {
          var renderQuality: LayerRenderer.RenderQuality {
              switch type {
              case .world: Constants.worldRenderQuality
              case .menu: Constants.menuRenderQuality
              }
          }
      }
      
      extension Renderer {
          func adjustRenderQuality(for scene: MyScene) {
              guard layerRenderer.configuration.isFoveationEnabled else {
                  return;
              }
              layerRenderer.renderQuality = scene.renderQuality
          }
      }
    • 16:58 - SwiftUI immersion style

      // SwiftUI immersion style
      
      @main
      struct MyApp: App {
          @State var immersionStyle: ImmersionStyle
      
          var body: some Scene {
              ImmersiveSpace(id: "MyImmersiveSpace") {
                  CompositorLayer(configuration: MyConfiguration()) { @MainActor layerRenderer in
                      Renderer.startRenderLoop(layerRenderer)
                  }
              }
              .immersionStyle(selection: $immersionStyle, in: .progressive, .full)
          }
      }
    • 17:12 - Layer configuration

      // Layer configuration
      
      struct MyConfiguration: CompositorLayerConfiguration {
          func makeConfiguration(capabilities: LayerRenderer.Capabilities,
                                 configuration: inout LayerRenderer.Configuration) {
              // Configure other aspects of LayerRenderer
              
              if configuration.layout == .layered {
                  let stencilFormat: MTLPixelFormat = .stencil8 
                  if capabilities.drawableRenderContextSupportedStencilFormats.contains(
                      stencilFormat
                  ) {
                      configuration.drawableRenderContextStencilFormat = stencilFormat 
                  }
                  configuration.drawableRenderContextRasterSampleCount = 1
              }
          }
      }
    • 17:40 - Render loop

      // Render loop
      
      struct Renderer {
          let portalStencilValue: UInt8 = 200 // Value not used in other stencil operations
      
          func renderFrame(with scene: MyScene,
                           drawable: LayerRenderer.Drawable,
                           commandBuffer: MTLCommandBuffer) {
              let drawableRenderContext = drawable.addRenderContext(commandBuffer: commandBuffer)
              let renderEncoder = configureRenderPass(commandBuffer: commandBuffer)
              drawableRenderContext.drawMaskOnStencilAttachment(commandEncoder: renderEncoder,
                                                                value: portalStencilValue)
              renderEncoder.setStencilReferenceValue(UInt32(portalStencilValue))
              
              scene.render(to: drawable, renderEncoder: renderEncoder)
      
              drawableRenderContext.endEncoding(commandEncoder: commandEncoder)
              drawable.encodePresent(commandBuffer: commandBuffer)
          }
      }
    • 20:55 - App structure

      // App structure
      
      @main
      struct MyImmersiveMacApp: App {
          @State var immersionStyle: ImmersionStyle = .full
      
          var body: some Scene {
              WindowGroup {
                  MyAppContent()
              }
      
              RemoteImmersiveSpace(id: "MyRemoteImmersiveSpace") {
                  MyCompositorContent()
              }
              .immersionStyle(selection: $immersionStyle, in: .full, .progressive)
         }
      }
    • 21:14 - App UI

      // App UI
      
      struct MyAppContent: View {
          @Environment(\.supportsRemoteScenes) private var supportsRemoteScenes
          @Environment(\.openImmersiveSpace) private var openImmersiveSpace
          @State private var spaceState: OpenImmersiveSpaceAction.Result?
      
          var body: some View {
              if !supportsRemoteScenes {
                  Text("Remote SwiftUI scenes are not supported on this Mac.")
              } else if spaceState != nil {
                  MySpaceStateView($spaceState)
              } else {
                  Button("Open remote immersive space") {
                      Task {
                          spaceState = await openImmersiveSpace(id: "MyRemoteImmersiveSpace")
                      }
                  }
              }
          }
      }
    • 21:35 - Compositor content and ARKit session

      // Compositor content and ARKit session
      
      struct MyCompositorContent: CompositorContent {
          @Environment(\.remoteDeviceIdentifier) private var remoteDeviceIdentifier
      
          var body: some CompositorContent {
              CompositorLayer(configuration: MyConfiguration()) { @MainActor layerRenderer in
                  guard let remoteDeviceIdentifier else { return }
                  let arSession = ARKitSession(device: remoteDeviceIdentifier)
                  Renderer.startRenderLoop(layerRenderer, arSession)
              }
          }
      }
    • 23:17 - C interoperability

      // Swift
      let remoteDevice: ar_device_t = remoteDeviceIdentifier.cDevice
Renderer.start_rendering(layerRenderer, remoteDevice)
      
      // C
      void start_rendering(cp_layer_renderer_t layer_renderer, ar_device_t remoteDevice) {
    ar_session_t session = ar_session_create_with_device(remoteDevice);
    // ...
}

Developer Footer

  • ビデオ
  • WWDC25
  • イマーシブなアプリを作成するためのMetalレンダリングの新機能
  • メニューを開く メニューを閉じる
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    メニューを開く メニューを閉じる
    • アクセシビリティ
    • アクセサリ
    • App Extension
    • App Store
    • オーディオとビデオ(英語)
    • 拡張現実
    • デザイン
    • 配信
    • 教育
    • フォント(英語)
    • ゲーム
    • ヘルスケアとフィットネス
    • アプリ内課金
    • ローカリゼーション
    • マップと位置情報
    • 機械学習とAI
    • オープンソース(英語)
    • セキュリティ
    • SafariとWeb(英語)
    メニューを開く メニューを閉じる
    • 英語ドキュメント(完全版)
    • 日本語ドキュメント(一部トピック)
    • チュートリアル
    • ダウンロード(英語)
    • フォーラム(英語)
    • ビデオ
    Open Menu Close Menu
    • サポートドキュメント
    • お問い合わせ
    • バグ報告
    • システム状況(英語)
    メニューを開く メニューを閉じる
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles(英語)
    • フィードバックアシスタント
    メニューを開く メニューを閉じる
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program(英語)
    • News Partner Program(英語)
    • Video Partner Program(英語)
    • セキュリティ報奨金プログラム(英語)
    • Security Research Device Program(英語)
    Open Menu Close Menu
    • Appleに相談
    • Apple Developer Center
    • App Store Awards(英語)
    • Apple Design Awards
    • Apple Developer Academy(英語)
    • WWDC
    Apple Developerアプリを入手する
    Copyright © 2025 Apple Inc. All rights reserved.
    利用規約 プライバシーポリシー 契約とガイドライン