View in English

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

クイックリンク

5 クイックリンク

ビデオ

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

その他のビデオ

ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。

  • 概要
  • トランスクリプト
  • コード
  • RealityKitによる空間描画アプリの構築

    空間描画アプリの構築プロセスで、RealityKitを使いこなしましょう。RealityKitとARKitおよびSwiftUIの統合により生まれる、印象的で魅力的な空間体験の構築について、RealityKitにおいてリソースが動作する仕組みや、ユーザーのブラシの線の更新を高速化する低レベルのメッシュおよびテクスチャのAPIなどの機能を使用する方法を解説します。

    関連する章

    • 0:00 - Introduction
    • 2:43 - Set up spatial tracking
    • 5:42 - Build a spatial 
user interface
    • 13:57 - Generate brush geometry
    • 26:11 - Create a splash screen

    リソース

    • Creating a spatial drawing app with RealityKit
    • Drawing paths and shapes
    • Forum: Spatial Computing
      • HDビデオ
      • SDビデオ

    関連ビデオ

    WWDC24

    • iOS、macOS、visionOS向けRealityKit APIの紹介
    • Reality Composer Proにおけるインタラクティブな3Dコンテンツの作成
    • RealityKitオーディオで空間コンピューティングアプリの質を向上
    • visionOSにおけるカスタムホバーエフェクトの作成

    WWDC23

    • 空間コンピューティングにおけるレンダリングの詳細
  • ダウンロード

    こんにちは Adrianです RealityKitチームのエンジニアです このセッションでは RealityKitの新機能を使用して visionOS用の空間描画アプリを 構築するプロセスについて説明します

    RealityKitは 高性能な 3Dシミュレーションとレンダリング機能を iOS macOS visionOSに提供する フレームワークです visionOSでは RealityKitは アプリの空間機能の基盤となります

    Apple Vision Proの発表以来 デベロッパの皆さんから 有益なフィードバックを多数受け取っており プラットフォームの機能を進化させながら そのフィードバックに対応するために 懸命に取り組んできました

    本日は RealityKitで作成できるアプリの 限界を押し上げる 新しいAPIを紹介します 詳細を確認しましょう

    Appleの空間描画アプリは RealityKitのパワフルな3Dの機能を SwiftUIおよびARKitと統合し 優れたユーザー体験を実現します カスタマイズされたメッシュやテクスチャ シェーダを構築して 洗練されたビジュアルデザインを 実現するのは 楽しい作業です

    アプリを起動すると 目を引くスプラッシュ画面が表示されます

    簡単な設定プロセスの後 作成の準備が整います

    空中で指をつまむだけで すぐに描画を開始できます

    ブラシストロークの外観は パレットビューで変更できます

    このアプリは チューブのようなソリッドブラシタイプと

    きらめくスパークルブラシを サポートしています

    ブラシストロークはカスタマイズ可能で ストロークの色や太さを変更できます

    他にもいろいろあります このアプリを一緒に構築していきましょう それでは始めましょう まず 空間トラッキングを設定して アプリが手と環境のデータを 認識できるようにします

    次にUIを作成して ブラシとキャンバスを 制御できるようにします また パワフルな新機能を使って ホバー時のUIの外観をカスタマイズします

    RealityKitでのメッシュの 仕組みを詳しく確認し アプリで新しいRealityKit APIを使用して Metalでブラシジオメトリを 効率的に生成する方法を紹介します

    動的なテクスチャと空間UI要素を使用して 魅力的なスプラッシュ画面を作成し アプリに最後の仕上げを施します

    描画アプリには つまんで手を動かして描くときの 手のポーズを認識させる必要があります そのためには ハンドアンカーの 空間トラッキングを設定します

    visionOSでは アプリは SwiftUIまたはRealityKitコンテンツを ウインドウ ボリューム スペースに 配置できます

    アプリでイマーシブ空間を使用すると アンカーを使用して空間トラッキング情報を 受け取ることができます

    これには ワールドアンカーと プレーンアンカーを使用した シーンの理解に関する情報と ハンドアンカーを使用した ポーズに関する情報が含まれます

    visionOS 1.0では ARKitを使って このデータにアクセスできました

    visionOS 2.0ではRealityKitアプリで 空間トラッキングを使用するための より簡単な方法を導入しています このAPIを使って 描画アプリで空間ハンドトラッキングを設定しましょう

    RealityKitでは AnchorEntityを使って RealityKitエンティティを ARアンカーに固定できます

    この描画アプリでは それぞれの手に 2つのAnchorEntityが作成されます 1つは親指の先端に固定され もう1つは人差し指の先端に固定されます

    空間トラッキングデータにアクセスするには アプリはユーザーからの許可を必要とします この描画アプリで ユーザーがをタップすると 関連する承認が要求されます

    重要なのは ユーザーに許可を求めるタイミングです

    承認はアプリが必要なときにのみ要求します ここでは ユーザーが 描画を開始するときです

    RealityKitでトラッキングデータの 承認を要求するには SpatialTrackingSessionを使用します これはvisionOS 2.0の新しいAPIです

    アプリに必要な トラッキング機能を宣言します この場合 アプリには手のデータが必要です

    次にSpatialTrackingSessionで runを呼び出します この時点で このトラッキングを 承認するためのアラートが表示されます

    run関数は未承認のトラッキング機能の リストを返します このリストをチェックして 許可の有無を確認できます

    空間トラッキングが承認された場合は AnchorEntityのtransformを介して トラッキングデータにアクセスできます

    許可が拒否された場合 AnchorEntityのtransformは 更新されません ただし AnchorEntityは ポーズを視覚的に更新します

    要点をまとめましょう ImmersiveSpaceを アプリで使用すると RealityKitコンテンツを 現実世界に固定できます

    AnchorEntityを使用すると RealityKitコンテンツで アンカーを設定できます

    そして今年からは アプリでAnchorEntityの transformにアクセスする必要がある場合 SpatialTrackingSessionを 使用できます

    さらにSpatialTrackingSessionを 使用すると AnchorEntitiesは RealityKitの物理システムと対話できます

    次は このアプリのユーザー インターフェイスについて説明します

    スプラッシュ画面でをタップすると イマーシブ空間に移動し 描画キャンバスが表示されます

    キャンバスのサイズや位置は 球形のハンドルをドラッグすることで 変更できます

    描画を開始する準備ができたら パレットビューが表示されます

    ここでブラシの形状と色を設定できます

    描画の準備ができたら キャンバス内に足を踏み入れるだけで 開始できます

    このインターフェイスの構築方法を 詳しく見ていきましょう

    まず キャンバス配置インターフェイスから 始めます このインターフェイスを使用すると 描画領域を定義できます

    キャンバス配置中に イマーシブ空間を構成する要素は 2つあります 床には3D形状でキャンバスの端が描かれ ハンドルを使用すると キャンバスの位置を変更できます

    まず境界メッシュについて考えてみましょう このメッシュはリアルタイムで生成されます 境界のサイズはスライダをドラッグして 変更できるためです

    メッシュは2つの円で定義されます 左の図に示すように 外側の円が緑色 内側の円が赤色になります

    この形状をSwiftUIパスとして定義できます

    円は360度広がる円弧です そのため半径が異なる円弧を2つ作成します

    次に 正規化された偶奇の 塗りつぶしモードを指定して 作成する形状を定義しています

    RealityKitでメッシュを生成するには 今年の新しいAPIを使用できます MeshResource extrudingです MeshResource extrudingは パワフルなAPIで 2Dベクトルコンテンツを 3Dモデルに変換できます

    必要なのは シェイプの奥行と 解像度を指定することだけです

    覚えておくべき重要な考慮事項が 1つあります visionOSでは RealityKitは フォビエーションされたレンダラを使用します 周辺視野内の画像の領域は 低解像度でレンダリングされます これにより アプリのパフォーマンスが最適化されます シーンに高コントラストの 薄いジオメトリが含まれていると アーティファクトのちらつきに 気づくかもしれません この例では リングが薄すぎます このような薄いジオメトリック要素は 避けてください

    高コントラストの領域では 特に注意が必要です

    この問題に対処するには ジオメトリの厚みを増やし 高コントラストの薄いエッジを削除します 左側ではアーチファクトのちらつきが 軽減されています

    空間コンテンツの エイリアシングについて詳しくは WWDC23の「Explore rendering for spatial computing」 をご覧ください

    次にキャンバスハンドルについて説明します 1つ指摘しておきたいことが あります このハンドルを見つめると 青いハイライト効果が生じます

    visionOSでは HoverEffectComponentは ユーザーがRealityKitのコンテンツを 見つめたときに視覚効果を追加します

    visionOS 1.0では HoverEffectComponentは デフォルトの スポットライト効果を使用します

    今年はさらに2種類のホバー効果を HoverEffectComponentに導入します ハイライト効果はエンティティに 均一なハイライト色を適用します

    HoverEffectComponentを ShaderGraphシェーダと 併用できるようになりました シェーダベースのホバー効果は 非常に柔軟性が高いため ホバー時のエンティティの外観を 正確に制御できます

    ハンドルの青いハイライトを実現できるのは ハイライトホバー効果のおかげです ハイライト効果を使用するには ドットハイライトを使用して HoverEffectComponentを初期化し ハイライト色を指定します

    強度の値を変更して ハイライトを より鮮やかにすることもできます

    キャンバス配置のUI要素が 背後の環境の上で 光っているように見えることに 気付いたかもしれません これは加算ブレンドモードで 設定されているためです 今年 RealityKitは UnlitMaterialや PhysicallyBasedMaterialなどの 組み込みマテリアルで 加算ブレンドモードを 新たにサポートするようになりました これを使うには まずProgramを作成し ブレンドモードをaddに設定します

    ユーザーが描画キャンバスを選択したら いよいよメインイベントです パレットビューが表示され ユーザーはブラシの設定を開始できます

    パレットビューはSwiftUIで構築されており ブラシのタイプとスタイルを カスタマイズできます パレットの下部には 選択できる プリセットブラシのセットがあります

    ブラシプリセットビューに 特に注目したいと思います 各ブラシプリセットサムネールは 実際に完全な3次元形状であることに 注意してください

    このメッシュは実際のブラシストロークと 同じ方法で生成されます SwiftUIとRealityKitは シームレスに統合されます ここでは各サムネールに RealityViewを使用します これにより RealityKitの すべての機能を活用できます

    ブラシプリセットを見つめると 目を引くホバー効果がアクティブになり ブラシに沿って紫色の光が流れます

    これは先ほど説明した シェーダベースのホバー効果です

    この効果をどのように実現したかを 詳しく見ていきましょう

    シェーダベースのホバー効果は シェーダグラフの Hover Stateノードで実現します このノードはシェーダに ホバー効果を統合するための 便利なツールを提供します

    例えば Intensityはシステムが提供する値で 視線の状態に基づいて 0から1の間でアニメーション化されます Intensity値を使用すると キャンバスハンドルで先ほど説明したような ハイライト効果を再現できます

    ただしプリセットビューでは もっと高度な効果を作りたいと思っています

    グロー効果は ブラシストロークの最初から最後まで ブラシメッシュに沿って 流れる必要があります

    この複雑な効果を実現するために シェーダグラフマテリアルを使います シェーダグラフを一緒に見ていきましょう

    Hover Stateノードの Time Since Hover Start プロパティを使用します これはホバーイベントが始まってからの 秒単位での値です

    これを使って グローハイライトの 曲線に沿った位置を定義します ホバーイベントが始まると グローの位置が曲線に沿って移動し始めます

    ブラシストロークのメッシュを生成する際 アプリはCurveDistanceという 属性を提供します アプリはUV1チャネルを介して 各頂点のCurveDistance値を提供します

    これは ブラシストロークの曲線距離を 視覚化したものです この値はストロークの長さに応じて 増加します

    シェーダはグローハイライトの位置を 曲線距離と比較します

    こうすることでシェーダは 現在のジオメトリに対する グローの位置を把握できます

    次に グロー効果のサイズを定義します

    現在のジオメトリは グロー位置の範囲内にあるときに光ります

    これでイージング曲線を追加できます これはグローがジオメトリ上を 移動するときの ホバー効果のIntensityを定義します

    最後の手順は 計算したIntensity値に応じて ホバー効果の色と 元のブラシストロークの色を 混合することです

    これは良くできています

    シェーダベースのホバー効果を使用するには ますシェーダ設定を使用して HoverEffectComponentを作成します

    次にShaderGraphMaterialを使用します これがHover Stateノードの 更新を受け取ります

    ユーザーがブラシを設定する方法を 構築したので 次はアプリのコアについて説明します 各ブラシストロークの ジオメトリを生成します

    大まかに言うと メッシュは頂点と それらを接続する三角形などの プリミティブの集合です

    各頂点は その頂点の位置やテクスチャ座標など 様々な属性に関連付けられています

    これらの属性はデータによって記述されます 例えば 各頂点の位置は3次元ベクトルです

    頂点データはGPUに送信できるように バッファに整理する必要があります

    ほとんどのRealityKitメッシュでは データはメモリ内で連続して整理されます したがってメモリ内では 頂点位置0の次に頂点位置1が続き その次に頂点2というように続きます 他のすべての頂点属性についても同様です インデックスバッファは別々に配置され これにはメッシュ内の各三角形の 頂点インデックスが含まれます

    RealityKitの標準メッシュレイアウトは 汎用性が高く 様々なユースケースに対応します ただし場合によっては ドメイン固有のアプローチの方が効率的です

    描画アプリはカスタムビルドの ジオメトリ処理パイプラインを使用して ユーザーのブラシストロークの メッシュを作成します

    例えば 各ブラシストロークを滑らかにして メッシュの曲率を改善します

    このアルゴリズムは最適化されているため ブラシストロークの末尾に 点を追加する処理が 可能な限り高速になります レイテンシを最小限に抑えることが重要です

    ブラシストロークメッシュでは 頂点のレイアウトに 1つのバッファが使用されます

    ただし標準的な メッシュレイアウトとは異なり 各頂点はその個々の全体が 順に記述されます

    そのため属性はインターリーブされます 最初の頂点の位置の次に その頂点の法線が続き 次に従接線が続くというように すべての属性が記述されるまで続きます その後で初めてバッファは 2番目以降の頂点の記述を開始します

    これとは対照的に 標準の頂点バッファは 各属性のすべてのデータを 連続してレイアウトします ブラシ頂点バッファのレイアウトは 描画アプリに特に便利です

    ブラシストロークを生成するとき アプリは常に頂点バッファの末尾に 頂点を追加します ブラシ頂点バッファは 古いデータの位置を変更せずに 新しい頂点を追加できることに 注意してください ただし標準の頂点バッファでこれを行うと バッファの増大に伴い ほとんどのデータを移動する必要があります ブラシ頂点には 標準レイアウトで表示されるものとは 異なる属性もあります

    位置 法線 従接線などの 一部の属性は標準です

    色 マテリアルプロパティ 曲線距離などはカスタム属性です

    アプリのコードでは ブラシの頂点は Metal Shading Languageの この構造体として表されます

    構造体の各エントリは 頂点の属性に対応します

    そこで問題に直面します 一方では 高性能ジオメトリエンジンの 頂点レイアウトを保持し 不要な変換やコピーを 避けたいと考えています しかし一方で ジオメトリエンジンのレイアウトは RealityKitの標準レイアウトと 互換性がありません 必要なのは GPUバッファを そのままRealityKitに取り込み RealityKitにその読み取り方法を 指示する方法です

    そして今 LowLevelMeshという 新しいAPIでそれが可能になりました

    LowLevelMeshを使うと 様々な方法で頂点データを配置できます

    頂点データには 4つの異なる Metalバッファを使用できます そのためRealityKitの標準レイアウトに似た レイアウトを使うことができます

    ただしバッファを複数用意すると 便利な場合もあります 例えば テクスチャ座標を 他の属性よりも頻繁に 更新する必要があるとします その場合 この動的データを 独自のバッファに移動する方が効率的です

    頂点バッファをインターリーブするように 並べ替えることができます インターリーブと非インターリーブの 組み合わせも可能です

    また 三角形ストリップなどの Metalプリミティブタイプも使用できます

    LowLevelMeshと そのカスタムバッファレイアウトが アプリにどのようなメリットをもたらすか 考えてみてください

    メッシュデータは 独自のカスタムレイアウトを持つ バイナリファイルから 取得されているかもしれません これでそのデータを 変換のオーバーヘッドなしで 直接RealityKitに転送できます

    またはデジタルコンテンツ作成ツールや CADアプリケーションで見られるような バッファレイアウトが事前定義された 既存のメッシュ処理パイプラインを RealityKitにブリッジする場合もあります

    LowLevelMeshは ゲームエンジンから RealityKitにメッシュデータを効率的に ブリッジする方法としても使用できます

    LowLevelMeshにより メッシュデータをRealityKitに 提供する方法の可能性が広がります 皆さんのアプリで何が実現できるか 楽しみにしています では コードでLowLevelMeshを 作成する方法について見ていきましょう

    これでアプリは 余分な変換や不要なコピーなしで 頂点バッファをそのまま LowLevelMeshに提供できます

    LowLevelMesh属性を使用して 頂点のレイアウト方法を記述します 迅速な拡張機能で属性リストを SolidBrushVertex構造体に設定します

    まず位置の属性を宣言します

    詳しく見ていきましょう 最初のステップはセマンティクスの定義です これはLowLevelMeshに 属性の解釈方法を指示します

    この場合 属性は位置なので そのセマンティクスを使います

    次に この属性の Metal頂点形式を定義します この場合 SolidBrushVertexの 定義と一致するように float3を選択する必要があります

    次に 属性のオフセットを バイト単位で指定します

    最後にレイアウトの インデックスを指定します 頂点レイアウトのリストに インデックスを付けます これについては後で説明します 描画アプリは単一のレイアウトのみを 使うため インデックス0を使用します

    次にその他のメッシュ属性を宣言します

    法線属性と従接線属性は 異なるメモリオフセットと セマンティクスが使用されることを除いて 位置に似ています

    色属性には半精度浮動小数点値を使用します 今年は任意のMetal頂点形式を LowLevelMeshで使用できます これには圧縮された頂点形式が含まれます

    他の2つのパラメータには セマンティクスUV1とUV3を使用します また今年新たに 最大8つのUVチャネルをLowLevelMesh で使用できるようになりました シェーダグラフのマテリアルは これらの値にアクセスできます これでLowLevelMeshオブジェクト 自体を作成できます これを行うには LowLevelMesh記述子を作成します LowLevelMesh記述子は 概念的には Metalの MTLVertexDescriptorに似ていますが RealityKitがメッシュを取り込むために 必要な情報も含まれています

    まず頂点とインデックスバッファに 必要な容量を宣言します

    次に頂点属性のリストを渡します これは前のスライドでまとめたリストです

    次に 頂点レイアウトのリストを作成します 各頂点属性はレイアウトの1つを使用します

    LowLevelMeshは頂点データ用に 最大4つのMetalバッファを提供します バッファインデックスは どのバッファを使用するかを宣言します

    次に バッファオフセットと 各頂点のストライドを指定します ほとんどの場合 ここで行ったように 1つのバッファのみを使用します

    これでLowLevelMeshを初期化できます

    最後のステップは パーツのリストを入力することです 各パーツはインデックスバッファの 領域にまたがります

    各メッシュパーツに 異なるRealityKit マテリアルインデックスを 割り当てることができます

    またここでは メモリ効率を向上させるため 三角形のストリップトポロジを使用します

    最後に LowLevelMeshから MeshResourceを作成し それをエンティティの ModelComponentに割り当てます

    LowLevelMeshの頂点データを 更新するときは withUnsafeMutableBytes APIを使用できます

    このAPIを使用すると 実際のバッファにアクセスできます さらに GPUに渡されて レンダリングされます そのためメッシュデータを更新する際の オーバーヘッドは最小限です

    例えば メッシュのメモリレイアウトが 事前にわかっているため bindMemoryを使用して 提供された生のポインタを バッファポインタに変換できます

    インデックスバッファデータについても 同様です LowLevelMeshインデックスバッファは withUnsafeMutableIndicesを使用して 更新できます

    LowLevelMeshが アプリのメッシュ処理パイプラインを 高速化する強力なツールであることは 既に説明しました LowLevelMeshを使用すると 頂点またはインデックスバッファの更新を GPU演算で バックアップすることもできます 例を見てみましょう

    これは描画アプリのスパークルブラシです ブラシストロークに追従する パーティクルフィールドを生成します このパーティクルフィールドは フレームごとに動的に更新されるため ソリッドブラシの場合とは異なる 更新スキームを使用します

    メッシュ更新の頻度と複雑さを考えると GPUを使うのは理にかなっています

    詳しく見ていきましょう スパークルブラシには 位置や色などのパーティクルごとの 属性のリストが含まれています 前と同様に curveDistanceパラメータと パーティクルのサイズも含まれています

    GPUパーティクルシミュレーションでは SparkleBrushParticle型を使用して 各パーティクルの属性と速度を追跡します アプリはシミュレーションに SparkleBrushParticlesの 補助バッファを使用します

    SparkleBrushVertex構造体は メッシュの頂点データに使用されます これには各頂点のUV座標が含まれており シェーダは3D空間でパーティクルを 方向付ける方法を理解できます パーティクルごとに 4つの頂点を持つ平面が作成されます

    スパークルブラシメッシュの更新用に 2つのバッファを維持する必要があります SparkleBrushParticleで埋められた パーティクルシミュレーションバッファと SparkleBrushVerticesを含む LowLevelMesh頂点バッファです

    ソリッドブラシと同様に 頂点バッファの仕様を LowLevelMesh属性のリストと 共に提供します

    属性のリストは SparkleBrushVertexの メンバーに対応します

    GPUでLowLevelMeshを 設定するときは Metalコマンドバッファと 演算コマンドエンコーダを使用します

    バッファが処理を終えると RealityKitは自動的に変更を適用します コードでは次のようになります 前に述べたように ここではパーティクルシミュレーションに Metalバッファを使用し 頂点バッファにLowLevelMeshを使用します

    Metalコマンドバッファと 演算コマンドエンコーダを設定します これでアプリは GPU演算カーネルを実行して メッシュを構築できるようになります

    LowLevelMeshでreplaceを呼び出して コマンドバッファを提供します

    Metalバッファが返されます この頂点バッファはRealityKitが レンダリングのために直接使用します

    シミュレーションをGPUに ディスパッチした後 コマンドバッファをコミットします コマンドバッファの処理が完了すると RealityKitは更新された頂点データの使用を 自動的に開始します

    高速で応答性の高い ブラシストローク生成により アプリの見た目が向上します では魅力的なスプラッシュ画面で アプリの最後の仕上げをしましょう

    スプラッシュ画面は ユーザーをアプリの世界へ迎え入れる 最適な方法です 楽しみながらアプリのビジュアルスタイルを 披露する機会にもなります

    アプリのスプラッシュ画面には 4つの視覚的要素があります

    ロゴタイプには 2つの異なるフォントを使用した 「RealityKit Drawing App」という 3Dテキストが含まれています

    ロゴマークも3D形状です

    下部には ユーザーに描画の開始を促す ボタンがあります

    そして背景には ユーザーの環境で光る 印象的なグラフィックがあります

    ロゴタイプの作成から始めましょう

    まずデフォルトのシステムフォントで 「RealityKit」のAttributed Stringを 作成します

    今年新たに RealityKitのMeshResourceを MeshResource extrudingを使用して AttributedStringから 作れるようになりました

    AttributedStringを使用しているので 異なるプロパティを持つテキスト行を 簡単に追加できます 「Drawing App」というテキストを 別のフォントで サイズを大きくして描きましょう

    次に段落スタイルを使用して テキストを中央揃えにします

    AttributedStringを使用して テキストのスタイルを 設定する方法については WWDC21の 「What’s new in Foundation」をご覧ください

    これまでに作成したテキストを 拡大してみましょう 現時点では3Dモデルが 少し平坦に見えるので カスタマイズしましょう これを行うには ShapeExtrusionOptions構造体を MeshResource extrudingに渡します

    より厚い3D形状を作成するために まず奥行きを大きくします 次に メッシュに 2つ目のマテリアルを追加します 前面 背面 側面に割り当てる マテリアルインデックスを指定できます

    最後に テキストを正面から見たとき アウトラインマテリアルがより目立つように 微細な面取りを施します ここでは面取り半径を 0.1ポイントに指定します

    このアプリでは MeshResource extrudingを使用して ロゴマークも生成します SwiftUIパスを使用するので 形状の定義方法には 非常に柔軟性があります ロゴマークは 一連のベジエ曲線として設定されます

    SwiftUIパスの詳細については SwiftUIチュートリアル 「Drawing paths and shapes」 をご覧ください

    次にスプラッシュ画面の 背景について説明します これはアプリの中でも 最も印象的な美的要素の1つです これを構築するために LowLevelTextureという 新しいAPIを使いました LowLevelTextureは LowLevelMeshと同じ 高速リソース更新セマンティクス を提供しますが テクスチャアセット用です

    スプラッシュ画面では LowLevelTextureを使用して ピル型シェイプが連なる 一種の形状記述を生成します この形状記述は テクスチャの赤チャネルに保存されます

    各ピルの内部には暗い領域があり ピルの外側は明るい領域です

    テクスチャの緑チャネルには スプラッシュ画面の ビネットの記述が保存されます

    このテクスチャは Reality Composer Proの シェーダグラフシェーダを介して 最終画像に解釈されます

    LowLevelTextureは そのDescriptorから作成します LowLevelTexture記述子は Metalの MTLTextureDescriptorに相当します LowLevelMeshと同様に LowLevelTextureは ピクセル形式とテクスチャの使用を 詳細に制御します そしてRealityKitで圧縮ピクセル形式を 使用できるようになりました このスプラッシュ画面で必要なのは 赤と緑のチャネルだけなので ピクセル形式RG16Floatを使用します

    記述子から LowLevelTextureを初期化できます 次に LowLevelTextureから RealityKitテクスチャリソースを作成します

    これでこのテクスチャを マテリアルで使用する準備ができました

    LowLevelMeshと同じように GPUでLowLevelTextureを更新します まず Metalコマンドバッファと 演算コマンドエンコーダを設定します

    次にコマンドバッファを使って LowLevelTexture.replaceを 呼び出します 演算シェーダで書き込むことができる Metalテクスチャが返されます

    最後に GPU演算をディスパッチし コマンドバッファをコミットします コマンドバッファの処理が完了すると Metalテクスチャが自動的に RealityKitに表示されます こうして完成したスプラッシュ画面の 見た目に非常に満足しています 目を引く背景とパーソナライズされた 3Dジオメトリを組み合わせることで 非常に独特な外観になっています このアプリに 最適な仕上げを施すことができました

    以上で終わりです 今日は RealityKitでインタラクティブな 空間描画アプリを構築しました RealityKit 空間トラッキングAPIを使用して ユーザーが空間のどこに描画するかを アプリで検出できるようにしました SwiftUIと高度なホバー効果を使用して ブラシとスタイルをカスタマイズするための インタラクティブな空間UIを構築しました RealityKitでのリソース更新の仕組みを学び 高度な低レベルAPIを使用して メッシュとテクスチャを インタラクティブに生成しました 最後に新しいAPIを使って 空間体験向けに 2Dベクトルグラフィックスを インポートしました

    今年のRealityKitの新機能の 詳細については 「Discover RealityKit APIs for iOS, macOS and visionOS」と 「Enhance your spatial computing app with RealityKit audio」をどうぞ

    皆さんの成果に期待しています WWDC24の他のセッションも お楽しみください

    • 4:18 - Using SpatialTrackingSession

      // Retain the SpatialTrackingSession while your app needs access
      
      let session = SpatialTrackingSession()
      
      // Declare needed tracking capabilities 
      let configuration = SpatialTrackingSession.Configuration(tracking: [.hand])
      
      // Request authorization for spatial tracking
      let unapprovedCapabilities = await session.run(configuration)
              
      if let unapprovedCapabilities, unapprovedCapabilities.anchor.contains(.hand) {
          // User has rejected hand data for your app.
          // AnchorEntities will continue to remain anchored and update visually
          // However, AnchorEntity.transform will not receive updates
      } else {
          // User has approved hand data for your app.
          // AnchorEntity.transform will report hand anchor pose
      }
    • 7:07 - Use MeshResource extrusion

      // Use MeshResource(extruding:) to generate the canvas edge
      
      let path = SwiftUI.Path { path in
          // Generate two concentric circles as a SwiftUI.Path
          path.addArc(center: .zero, radius: outerRadius,
              startAngle: .degrees(0), endAngle: .degrees(360),
              clockwise: true)
          path.addArc(center: .zero, radius: innerRadius,
              startAngle: .degrees(0), endAngle: .degrees(360),
              clockwise: true)
      }.normalized(eoFill: true)
      var options = MeshResource.ShapeExtrusionOptions()
      options.boundaryResolution 
          = .uniformSegmentsPerSpan(segmentCount: 64)
      options.extrusionMethod = .linear(depth: extrusionDepth)
      
      return try MeshResource(extruding: path, 
                              extrusionOptions: extrusionOptions)
    • 9:33 - Highlight HoverEffectComponent

      // Use HoverEffectComponent with .highlight
      
      let placementEntity: Entity = // ...
       
      let hover = HoverEffectComponent(
          .highlight(.init(
              color: UIColor(/* ... */),
              strength: 5.0)
          )
      )
      
      placementEntity.components.set(hover)
    • 9:54 - Using Blend Modes

      // Create an UnlitMaterial with Additive Blend Mode
      
      var descriptor = UnlitMaterial.Program.Descriptor()
      descriptor.blendMode = .add
      
      let prog = await UnlitMaterial.Program(descriptor: descriptor)
      var material = UnlitMaterial(program: prog)
      
      material.color 
          = UnlitMaterial.BaseColor(tint: UIColor(/* ... */))
    • 13:45 - Shader based hover effects

      // Use shader-based hover effects
      
      let hoverEffectComponent = HoverEffectComponent(.shader(.default))
      entity.components.set(hoverEffectComponent)
      
      let material = try await ShaderGraphMaterial(named: "/Root/SolidPresetBrushMaterial",
                                                   from: "PresetBrushMaterial",
                                                   in: realityKitContentBundle)
      
      entity.components.set(ModelComponent(mesh: /* ... */, materials: [material]))
    • 16:56 - Defining a vertex buffer struct for the solid brush

      struct SolidBrushVertex {
          packed_float3 position;
          packed_float3 normal;
          packed_float3 bitangent;
          packed_float2 materialProperties;
          float curveDistance;
          packed_half3 color;
      };
    • 19:27 - Defining LowLevelMesh Attributes for solid brush

      extension SolidBrushVertex {
          static var vertexAttributes: [LowLevelMesh.Attribute] {
              typealias Attribute = LowLevelMesh.Attribute
              return [
                  Attribute(semantic: .position, format: MTLVertexFormat.float3, layoutIndex: 0,
                            offset: MemoryLayout.offset(of: \Self.position)!),
                  Attribute(semantic: .normal, format: MTLVertexFormat.float3, layoutIndex: 0,
                            offset: MemoryLayout.offset(of: \Self.normal)!),
                  Attribute(semantic: .bitangent, format: MTLVertexFormat.float3, layoutIndex: 0,
                            offset: MemoryLayout.offset(of: \Self.bitangent)!),
                  Attribute(semantic: .color, format: MTLVertexFormat.half3, layoutIndex: 0,
                            offset: MemoryLayout.offset(of: \Self.color)!),
                  Attribute(semantic: .uv1, format: MTLVertexFormat.float, layoutIndex: 0,
                            offset: MemoryLayout.offset(of: \Self.curveDistance)!),
                  Attribute(semantic: .uv3, format: MTLVertexFormat.float2, layoutIndex: 0,
                            offset: MemoryLayout.offset(of: \Self.materialProperties)!)
              ]
          }
      }
    • 21:14 - Make LowLevelMesh

      private static func makeLowLevelMesh(vertexBufferSize: Int, indexBufferSize: Int, 
                                           meshBounds: BoundingBox) throws -> LowLevelMesh
      {
          var descriptor = LowLevelMesh.Descriptor() // Similar to MTLVertexDescriptor
          
          descriptor.vertexCapacity = vertexBufferSize
          descriptor.indexCapacity = indexBufferSize
          descriptor.vertexAttributes = SolidBrushVertex.vertexAttributes
              
          let stride = MemoryLayout<SolidBrushVertex>.stride
          descriptor.vertexLayouts = [LowLevelMesh.Layout(bufferIndex: 0, 
                                                          bufferOffset: 0, bufferStride: stride)]
         
          let mesh = try LowLevelMesh(descriptor: descriptor)
          
          mesh.parts.append(LowLevelMesh.Part(indexOffset: 0, indexCount: indexBufferSize,
                                              topology: .triangleStrip, materialIndex: 0,
                                              bounds: meshBounds))
          return mesh
      }
    • 22:28 - Creating a MeshResource

      let mesh: LowLevelMesh
      
      let resource = try MeshResource(from: mesh)
      
      entity.components[ModelComponent.self] = ModelComponent(mesh: resource, materials: [...])
    • 22:37 - Updating vertex data of LowLevelMesh using withUnsafeMutableBytes API

      let mesh: LowLevelMesh
      
      mesh.withUnsafeMutableBytes(bufferIndex: 0) { buffer in
          let vertices: UnsafeMutableBufferPointer<SolidBrushVertex>
              = buffer.bindMemory(to: SolidBrushVertex.self)
      
          // Write to vertex buffer `vertices`
      }
    • 23:07 - Updating LowLevelMesh index buffers using withUnsafeMutableBytes API

      let mesh: LowLevelMesh
      
      mesh.withUnsafeMutableIndices { buffer in
          let indices: UnsafeMutableBufferPointer<UInt32>
              = buffer.bindMemory(to: UInt32.self)
      
          // Write to index buffer `indices`
      }
    • 23:58 - Creating a particle brush using LowLevelMesh

      struct SparkleBrushAttributes {
          packed_float3 position;
          packed_half3 color;
          float curveDistance;
          float size;
      };
      
      // Describes a particle in the simulation
      struct SparkleBrushParticle {
          struct SparkleBrushAttributes attributes;
          packed_float3 velocity;
      };
      
      // One quad (4 vertices) is created per particle
      struct SparkleBrushVertex {
          struct SparkleBrushAttributes attributes;
          simd_half2 uv;
      };
    • 24:58 - Defining LowLevelMesh Attributes for sparkle brush

      extension SparkleBrushVertex {
          static var vertexAttributes: [LowLevelMesh.Attribute] {
              typealias Attribute = LowLevelMesh.Attribute
              return [
                  Attribute(semantic: .position, format: .float3, layoutIndex: 0,
                            offset: MemoryLayout.offset(of: \Self.attributes.position)!),
      
                  Attribute(semantic: .color, format: .half3, layoutIndex: 0,
                            offset: MemoryLayout.offset(of: \Self.attributes.color)!),
                  
                  Attribute(semantic: .uv0, format: .half2, layoutIndex: 0,
                            offset: MemoryLayout.offset(of: \Self.uv)!),
                  
                  Attribute(semantic: .uv1, format: .float, layoutIndex: 0,
                            offset: MemoryLayout.offset(of: \Self.attributes.curveDistance)!),
                  
                  Attribute(semantic: .uv2, format: .float, layoutIndex: 0,
                            offset: MemoryLayout.offset(of: \Self.attributes.size)!)
              ]
          }
      }
    • 25:28 - Populate LowLevelMesh on GPU

      let inputParticleBuffer: MTLBuffer
      let lowLevelMesh: LowLevelMesh
      
      let commandBuffer: MTLCommandBuffer
      let encoder: MTLComputeCommandEncoder
      let populatePipeline: MTLComputePipelineState
      
      commandBuffer.enqueue()
      encoder.setComputePipelineState(populatePipeline)
      
      let vertexBuffer: MTLBuffer = lowLevelMesh.replace(bufferIndex: 0, using: commandBuffer)
      
      encoder.setBuffer(inputParticleBuffer, offset: 0, index: 0)
      encoder.setBuffer(vertexBuffer, offset: 0, index: 1)
      encoder.dispatchThreadgroups(/* ... */)
      
      // ...
      encoder.endEncoding()
      commandBuffer.commit()
    • 27:01 - Use MeshResource extrusion to generate 3D text

      // Use MeshResource(extruding:) to generate 3D text
      
      var textString = AttributedString("RealityKit")
      textString.font = .systemFont(ofSize: 8.0)
      
      let secondLineFont = UIFont(name: "ArialRoundedMTBold", 
                                  size: 14.0)
      let attributes = AttributeContainer([.font: secondLineFont])
      
      textString.append(AttributedString("\nDrawing App", 
                                         attributes: attributes))
      
      let paragraphStyle = NSMutableParagraphStyle()
      paragraphStyle.alignment = .center
      let centerAttributes 
          = AttributeContainer([.paragraphStyle: paragraphStyle])
      textString.mergeAttributes(centerAttributes)
      
      var extrusionOptions = MeshResource.ShapeExtrusionOptions()
      extrusionOptions.extrusionMethod = .linear(depth: 2)
      extrusionOptions.materialAssignment
              = .init(front: 0, back: 0, extrusion: 1,
                      frontChamfer: 1, backChamfer: 1)
      extrusionOptions.chamferRadius = 0.1
      
      let textMesh = try await MeshResource(extruding: textString
                                extrusionOptions: extrusionOptions)
    • 28:25 - Use MeshResource extrusion to turn a SwiftUI Path into 3D mesh

      // Use MeshResource(extruding:) to bring SwiftUI.Path to 3D
      
      let graphic = SwiftUI.Path { path in
          path.move(to: CGPoint(x: -0.7, y: 0.135413))
          path.addCurve(to: CGPoint(x: -0.7, y: 0.042066),
                        control1: CGPoint(x: -0.85, y: 0.067707),
                        control2: CGPoint(x: -0.85, y: 0.021033))
          // ...
      }
      
      var options = MeshResource.ShapeExtrusionOptions()
      // ...
      
      let graphicMesh = try await MeshResource(extruding: graphic
                                extrusionOptions: options)
    • 29:44 - Defining a LowLevelTexture

      let descriptor = LowLevelTexture.Descriptor(pixelFormat: .rg16Float,
                                                  width: textureResolution, 
                                                  height: textureResolution,
                                                  textureUsage: [.shaderWrite, .shaderRead])
      
      let lowLevelTexture = try LowLevelTexture(descriptor: descriptor)
      var textureResource = try TextureResource(from: lowLevelTexture)
      
      var material = UnlitMaterial()
      material.color = .init(tint: .white, texture: .init(textureResource))
    • 30:27 - Update a LowLevelTexture on the GPU

      let lowLevelTexture: LowLevelTexture
      
      let commandBuffer: MTLCommandBuffer
      let encoder: MTLComputeCommandEncoder
      let computePipeline: MTLComputePipelineState
      
      commandBuffer.enqueue()
      encoder.setComputePipelineState(computePipeline)
      
      let writeTexture: MTLTexture = lowLevelTexture.replace(using: commandBuffer)
      encoder.setTexture(writeTexture, index: 0)
      
      // ...
      
      encoder.dispatchThreadgroups(/* ... */)
      encoder.endEncoding()
      commandBuffer.commit()
  • 特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。

    クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。

Developer Footer

  • ビデオ
  • WWDC24
  • RealityKitによる空間描画アプリの構築
  • メニューを開く メニューを閉じる
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    メニューを開く メニューを閉じる
    • アクセシビリティ
    • アクセサリ
    • App Extension
    • App Store
    • オーディオとビデオ(英語)
    • 拡張現実
    • デザイン
    • 配信
    • 教育
    • フォント(英語)
    • ゲーム
    • ヘルスケアとフィットネス
    • アプリ内課金
    • ローカリゼーション
    • マップと位置情報
    • 機械学習
    • オープンソース(英語)
    • セキュリティ
    • 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.
    利用規約 プライバシーポリシー 契約とガイドライン