ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
RealityKitによる空間コンピューティンングアプリの拡張
RealityKitを使い、ウインドウの枠を超えて魅力的でイマーシブな3Dコンテンツを実現する方法を学びましょう。SwiftUIシーンとRealityViewの連携機能や、コンテンツをエンティティ階層に埋め込む方法について紹介します。また、アンカーを使って仮想コンテンツと実世界をブレンドさせたり、アプリにパーティクルエフェクトを取り入れたり、動画コンテンツを追加したり、ポータルでよりイマーシブな体験を作成する方法についても説明します。
関連する章
- 0:00 - Introduction
- 2:05 - RealityView attachments
- 6:11 - Video playback
- 10:59 - Portals
- 15:04 - Particle emitters
- 17:06 - Anchors
- 19:28 - Wrap-up
リソース
関連ビデオ
WWDC23
-
ダウンロード
♪ 心地よいヒップホップ音楽 ♪ ♪ こんにちは Yujinです RealityKitチームのエンジニアです 本日は空間コンピューティング アプリを強化するための RealityKitの新しい機能をご紹介します RealityKitが2019年に リリースされて以来 豊富な機能セットを活用するアプリが すばらしい体験を提供しているのを 見てきました 空間コンピューティングは RealityKitにさらに機能を追加しました ポータル パーティクルエミッタ RealityViewアタッチメントや 他にもたくさんあります 「Build spatial experiences with RealityKit」のセッションでは RealityKitの基本要素 コンテナオブジェクトであるエンティティ エンティティで特定の動作を定義する コンポーネント エンティティとコンポーネントの 両方に従って機能を追加する システムについて説明しました RealityView APIは SwiftUIとRealityKitの 橋渡しの役割をすることを説明し インタラクション アニメーション 空間オーディオを RealityKitシーンに追加する 方法も説明しました まだご覧でない場合は このセッションをぜひご覧ください 本日のセッションでは アプリをさらに魅力的で イマーシブにするために役立つ RealityKitの新機能をご紹介します まず RealityViewの アタッチメントを使用して SwiftUIビューを RealityKitに埋め込む方法を学びます 次にRealityKitシーン内に 動画再生機能を追加する方法を説明し ポータルを使用して異次元への ウインドウを開く方法を説明します パーティクルエミッタAPIを使って 視覚効果でシーンを向上する方法を お見せします そして最後にRealityKitの アンカーを使って 3Dコンテンツを壁などの 実世界の場所に 配置する方法をお見せします では RealityViewアタッチメントから 始めましょう アタッチメントはSwiftUIコンテンツを RealityKitシーンに埋め込むための 便利な方法です この例のアプリでは アタッチメントを使用して 地球と月のモデルの下に テキストラベルを加えます また 月が私たちの海の波に どう影響を及ぼすかを説明する ビューもアタッチしました これをコードで行う方法を見てみましょう アプリ内でRealityViewを使って 地球モデルをレンダリングします RealityViewは RealityKitエンティティを 追加するためのビューです エンティティはレンダリング アニメーション シミュレーションを行うために RealityViewに追加される必要があります ここでは地球のエンティティを読み込み RealityViewのコンテンツに追加しました ではアタッチメントを使用するために RealityViewに変更を加えましょう アタッチメントは RealityKitコンテンツに関連する 特定の場所に配置されるビューです アタッチメントの設定には 2つの要素があります 1つはRealityViewの makeクロージャに 追加されたパラメータがあります 2つ目はRealityViewに追加された アタッチメントビュービルダーです 最初にアタッチメントビュービルダーから 説明しましょう ここではRealityKitコンテンツに 追加したい SwiftUIビューを提供します この例では地球にラベルを付けるために テキストビューを追加しました また ビューがエンティティとして makeクロージャに送信されたときに 特定できるように タグ修飾子をビューに追加します このタグは任意のハッシュ値にできます ここではearth_labelという文字列を 使用しました RealityViewのmakeクロージャには アタッチメントのパラメータには ビューが含まれ エンティティとして表現されました ビューをエンティティの形にするには 「entity(for:)」をアタッチメントで 呼出し ビュービルダーで提供した同じタグ earth_labelをパスします その結果 ビューアタッチメント エンティティを取得し それを他のエンティティと同じように RealityKitコンテンツに追加できます ラベルを地球の下に表示するには アタッチメントを地球エンティティの 子として追加し 少し下に配置します 追加したい他のすべてのアタッチメントにも それぞれに異なるタグを使用して このプロセスを繰り返します Xcodeで見てみましょう このサンプルのアプリでは 3つの アタッチメントをRealityViewに追加します 地球の下にラベルを追加し 月にもラベルを付けます そして潮汐に及ぼす 月の軌道の影響を説明する 短い文章を追加します これはSwiftUIの glassBackgroundEffectで スタイルづけます RealityViewのmakeクロージャに コンテンツに対応する エンティティを追加します まず地球の下にearthAttachmentを 追加します 月にも同じことを行います 最後に潮汐の説明を コンテナエンティティの左に追加します アプリを構築して実行すると 作成したアタッチメントが モデルの隣に表示されています アタッチメントのデータの流れを おさらいしましょう アタッチメントはRealityViewの アタッチメントビュービルダーで 始まります ここでRealityKitシーンに追加したい SwiftUIビューを追加できます RealityViewのmakeクロージャで そのアタッチメントを エンティティとして取得し シーンに追加します 更新クロージャ内のエンティティを 更新することもできます このクロージャはSwiftUIビューの状態が 変更した場合に呼び出されます これはRealityViewで 動的に変更するコンテンツに 応答する場合に使用します アタッチメントの使用方法の詳細については 「Work with Reality Composer Pro content in Xcode」の セッションをご覧ください RealityViewアタッチメントは その他のUI要素の テキストコンテンツをシーンに追加するのに 便利な方法です さらに動画を追加して アプリをより魅了的なものに することができます これを行うには VideoPlayerComponentを使用します 動画再生コンポーネントはRealityKitの 新しいコンポーネントタイプで 3Dシーン内に動画コンテンツを 埋め込む場合に使用します ご存知かと思いますが コンポーネントはエンティティに 追加できる特定の動作を定義します VideoPlayerComponentを使って 動画を再生するには 最初に 動画ファイルを リソースバンドルから読み込みます 次にそれを使って AVPlayerインスタンスを作成します これでVideoPlayerComponentを 作成できるようになります VideoPlayerComponentを エンティティに添付すると 動画の アスペクト比に合致する正方形のメッシュが 自動的に生成されます この動作は SwiftUIのVideoPlayerや Core AnimationのAVPlayerLayerなど 既存の動画再生APIと類似しています しかしRealityKitは 3Dフレームワークであるため 動画は3D空間で移動したり 配置したりできるように メッシュのある エンティティとして表現されます AV Foundationによりサポートされる すべての動画形式は VideoPlayerComponentで機能します これにはMV-HEVCを使用する 2D動画形式と3D動画が含まれます 最後に VideoPlayerComponentは AVPlayerを介して 提供されるキャプションを 自動的に表示します 3D動画を含む 自身の動画コンテンツを 作成する方法については 「Deliver video content for spatial experiences」の セッションをご覧ください RealityKitシーンに動画を追加するには まず 動画アセットへのURLを使用して AVPlayerItemを作成します 次にAVPlayerを作成します エンティティに 作成したばかりのAVPlayerで 初期化されたVideoPlayerComponentを 追加します VideoPlayerComponentは 動画のアスペクト比に基づいたサイズの メッシュを自動的に生成します RealityKitは実世界の単位で機能するため 動画はデフォルトで 高さが1メートルになります 異なるサイズの動画にするには エンティティをスケーリングします 私は高さ40センチの動画にしたいので エンティティを0.4で乗算します これで動画を再生する準備ができました 現在のアイテムをAVPlayerItemに設定し AVPlayerで再生を呼び出します このコードでアプリを再構築して 実行してみましょう アプリに動画エンティティを シーンに追加する 「詳細」ボタンを追加しました ボタンをクリックすると 不透明度コンポーネントと fromToByAnimationを使って 動画にフェードインします 例として動画のコンテンツに 地球の潮汐に対する月の引力の役割を 説明する 短いクリップを作成しました 見てみましょう 月は地球を周回します 月の引力は海に大きな影響を及ぼし 月に向かって わずかに隆起させます VideoPlayerComponentは キャプションに対するシステム全体の 設定を尊重します アクセシビリティセクションにある 「設定」アプリでオンにしてみましょう 地球と月の間の絶え間ない相互作用により 1日に二度 潮の満ち引きが起こります また VideoPlayerComponentは パススルーティントもサポートします この機能が有効になると パススルーコンテンツは 動画の色と一致するように調整されます これはこのプラットフォームで TVアプリ内の映画やTV番組を 視聴する場合に使用されるのと同じ処理です パススルーティントを使用するには isPassthroughTintingEnabled プロパティを「true」に設定します コンテンツタイプ 視聴モード 動画サイズなど VideoPlayerComponentの プロパティが変更された場合に 通知を受けるようにVideoPlayerEventsを サブスクライブできます イベントをサブスクライブするには RealityViewコンテンツの サブスクライブ機能を呼び出し イベントタイプとエンティティを指定します イベントハンドラクロージャ内の イベントに応答することができます VideoPlayerComponentの追加は 3Dシーンに最適です 今のところアプリは地球と月の モデルを特徴としていますが 宇宙を背景にして 表示したいと思います 宇宙に存在する月の軌道を 部屋のマジックウインドウに 表示できればすごくかっこよくなると 思います これはポータルを使ってシーンを レンダリングすることで実現します ポータルはメッシュサーフェスを介して 見ることのできる 別世界へのオープニングを作成します この世界の中にあるエンティティは 別の照明を使用し ポータルの幾何学でマスキングされています この例ではRealityKitの 特徴的な3つの機能を 実演しています まず 宇宙のシーンのレンダリングには ポータルが使用されています 次に ポータルの縁を装飾するために パーティクルエフェクトが使用され 最後に 部屋の壁にポータルを 配置するために アンカーが使用されています ではポータルから始めましょう ポータルを作成するには 最初にWorldを作る必要があります これを行うためには Worldコンポーネントを持つシーンに エンティティを追加します このコンポーネントは そのエンティティツリーが 別世界に属することを示しています Worldエンティティは ポータルサーフェスを介してのみ 見ることができます コンテンツをWorldに追加するには エンティティをWorldエンティティの 子として追加します ここでは 空 地球 月のモデルに加え ImageBasedLightを追加して Worldの中の照明を定義します Worldエンティティのすべての子孫は このWorldの中にのみ現れます 次にポータルを作成します そのためにはモデルコンポーネントのある エンティティを追加します モデルコンポーネントには メッシュとマテリアルの 2つのプロパティがあります メッシュには円形の平面を生成して ポータルの表面にします マテリアルにはメッシュが ポータルとして表示されるように 新しいポータルマテリアルを割り当てます ポータルをWorldとつなげるために ポータルコンポーネントを エンティティに追加し Worldエンティティの ターゲットプロパティを設定します これにより ポータルはマスクとして機能し Worldの中のコンテンツを明らかにします これをコードで見てみましょう RealityViewで makeWorldとmakePortalを実装する 2つの関数の呼び出しを追加しました makeWorld関数では Worldエンティティを作成し ポータルのコンテンツを追加します makePortal関数では ポータルと今作成したWorldへの リンクを作成します 最後にこの2つのエンティティを RealityViewのコンテンツに追加します それぞれの関数を詳細に見てみましょう makeWorld関数内に エンティティを作成し WorldComponentをアタッチしました 次に EnvironmentResourceをロードし ImageBasedLightとして使用します ImageBasedLightコンポーネントと ImageBasedLight ReceiverComponentを使用して これをWorldに適用します RealityKitの画像ベースの 照明の詳細については 「Explore rendering for spatial computing」の セッションをご覧ください 次にWorldにコンテンツを追加します 地球 月 空のモデルをロードして 子としてWorldに追加します これらのエンティティはWorldの子であるため ポータルを介してのみ見ることができます では makePortal関数に移りましょう ポータルを作成するには まずメッシュが必要です エンティティのモデルコンポーネントを 作成してメッシュを作成します ポータルを円状にするために ディメンションが等しく コーナー半径の大きさが半分の 平面を作成します また PortalMaterialを作成して ModelComponentの マテリアルとして使用します 最後に先程作成したWorldエンティティで 初期化されたポータルコンポーネントを アタッチします これによりポータルとWorldがつながり メッシュを介してWorldのコンテンツを 見れるようになります ではパーティクルエフェクトで ポータルの縁を装飾しましょう これはRealityKitで提供される ParticleEmitterComponentで行います パーティクルエミッタはRealityKitの さまざまな視覚効果を表現するのに 使用できます スパークス スノー インパクトエフェクトなどがそうです パーティクルエミッタは Reality Composer Proを使うか RealityKitを使用してランタイムで ParticleEmitterComponentを介して 作成できます ここでは Reality Composer Proを使用して パーティクルアセットを用意しました これを使って先程作成した ポータルを装飾できます これをシーンにロードし RealityKitを使用して ランタイムでパーティクル プロパティを変更してみましょう パーティクルを経時的に更新するために ParticleTransitionSystemという カスタムシステムを作成しました ここでは EntityQueryを使用して ParticleEmitterComponentを持つ エンティティを見つけます システム更新内でクエリを実行し 結果のエンティティを反復します 各エンティティでupdateParticles 関数を呼び出します これは次に実装します RealityKitの カスタムシステムの詳細については 「Build spatial experiences with RealityKit」の セッションをご覧ください updateParticles関数内で ParticleEmitterComponentを エンティティから取得します ParticleEmitterComponentには パーティクルの外観と動作の さまざまな側面を制御する 多くのプロパティが含まれています ここで エンティティのスケールに基づいて lifeSpanとvortexStrength プロパティを作成します そうすることでエンティティの サイズが大きくなるとともに ポータルでパーティクルが より速く回転するようになります 最後にコンポーネントをエンティティに 再割り当てして変更を適用します これで完了しました パーティクルエミッタの 異なるプロパティの詳細については 「Meet Reality Composer Pro」を ご覧ください あともう少しで アプリの最終仕上げが完了します 最後に部屋の壁のポータルを 取り付けましょう これはRealityKitのアンカーを 使って行います アンカーは壁や床 または頭や手の近くに コンテンツを配置するために使用できます RealityKitのアンカーは .continuousと.once.の 2つのトラッキングモードをサポートします 連続トラッキングモードを使用する場合 アンカーエンティティは 頭が動いた場合など アンカーとともに経時的に移動します 1回トラッキングモードを使用する場合 アンカーエンティティは 一度配置されると動かなくなります エンティティが固定されたことを 聞きたい場合は RealityKitで AnchoredStateChangedイベントを サブスクライブします 3Dコンテンツを配置するために 親エンティティに アンカーを使うことは可能ですが アンカー自体が明確に変換されるのは ユーザーのプライバシーを保護するために アプリでは可視化されません アンカーの変換にアクセスするには ARKitを使用する必要があります これについての詳細は 「Meet ARKit for spatial computing」をご覧ください アプリでアンカーを使用するには イマーシブスペースで使用できるように 最初にアプリを変更する必要があります イマーシブスペースは 特別なタイプのコンテナで アプリがウインドウ枠外でコンテンツを レンダリングするのを可能にします これを行うには ImmersiveSpaceを SwiftUIシーンに追加します また 「.immersionStyle」 修飾子を追加し 「in: .mixed」に設定します ImmersiveSpace内に RealityViewを使用して アンカーされるコンテンツを配置します イマーシブスペースの詳細については 「Go beyond the window with SwiftUI」 のセッションをご覧ください RealityView内で アンカーエンティティを ポータルのコンテナとして使用できます アンカーエンティティを アンカーしたいコンテンツの 表面のタイプの仕様で初期化します ここでは 最小サイズが 1平方メートルの 縦の壁にしたいと思います 仕様に一致するアンカーが見つかると RealityKitはコンテンツを 自動的に壁に配置します これで完了です アプリを実行すると 壁に配置された ポータルが表示されました ポータルとパーティクルから アンカーとアタッチメントまで RealityKitはイマーシブ体験を構築するための さまざまな機能を提供してくれます では今日のセッションの内容を おさらいしましょう RealityViewのアタッチメントは SwiftUIコンテンツを エンティティ階層内に埋め込み UI要素と3D要素の配置を可能にします VideoPlayerComponent ポータル パーティクルエフェクトは RealityKitのシーンを向上するための 動的な要素です 最後に アンカーは 3Dコンテンツを床や壁などの 実世界の表面に配置することができます 「Build spatial experiences with RealityKit」のセッションでは エンティティ コンポーネント RealityViewなどの 主要な概念を説明しています 「Work with Reality Composer Pro content in Xcode」では Reality Composer Proと RealityKitを使用して イマーシブアプリを構築するための プロセスを説明しています RealityKitのこれらの新機能を使って 皆さんが作成するアプリが楽しみです ご視聴ありがとうございました ♪
-
-
2:30 - Attachments
import SwiftUI import RealityKit struct MoonOrbit: View { var body: some View { RealityView { content, attachments in guard let earth = try? await Entity(named: "Earth") else { return } content.add(earth) if let earthAttachment = attachments.entity(for: "earth_label") { earthAttachment.position = [0, -0.15, 0] earth.addChild(earthAttachment) } } attachments: { Attachment(id: "earth_label") { Text("Earth") } } } }
-
8:03 - VideoPlayerComponent
public func makeVideoEntity() -> Entity { let entity = Entity() let asset = AVURLAsset(url: Bundle.main.url(forResource: "tides_video", withExtension: "mp4")!) let playerItem = AVPlayerItem(asset: asset) let player = AVPlayer() entity.components[VideoPlayerComponent.self] = .init(avPlayer: player) entity.scale *= 0.4 player.replaceCurrentItem(with: playerItem) player.play() return entity }
-
10:05 - Passthrough tinting
var videoPlayerComponent = VideoPlayerComponent(avPlayer: player) videoPlayerComponent.isPassthroughTintingEnabled = true entity.components[VideoPlayerComponent.self] = videoPlayerComponent
-
10:40 - VideoPlayerEvents
content.subscribe(to: VideoPlayerEvents.VideoSizeDidChange.self, on: entity) { event in // ... }
-
13:12 - Portal
struct PortalView : View { var body: some View { RealityView { content in let world = makeWorld() let portal = makePortal(world: world) content.add(world) content.add(portal) } } } public func makeWorld() -> Entity { let world = Entity() world.components[WorldComponent.self] = .init() let environment = try! EnvironmentResource.load(named: "SolarSystem") world.components[ImageBasedLightComponent.self] = .init(source: .single(environment), intensityExponent: 6) world.components[ImageBasedLightReceiverComponent.self] = .init(imageBasedLight: world) let earth = try! Entity.load(named: "Earth") let moon = try! Entity.load(named: "Moon") let sky = try! Entity.load(named: "OuterSpace") world.addChild(earth) world.addChild(moon) world.addChild(sky) return world } public func makePortal(world: Entity) -> Entity { let portal = Entity() portal.components[ModelComponent.self] = .init(mesh: .generatePlane(width: 1, height: 1, cornerRadius: 0.5), materials: [PortalMaterial()]) portal.components[PortalComponent.self] = .init(target: world) return portal }
-
15:50 - Adding particles around the portal
public class ParticleTransitionSystem: System { private static let query = EntityQuery(where: .has(ParticleEmitterComponent.self)) public func update(context: SceneUpdateContext) { let entities = context.scene.performQuery(Self.query) for entity in entities { updateParticles(entity: entity) } } } public func updateParticles(entity: Entity) { guard var particle = entity.components[ParticleEmitterComponent.self] else { return } let scale = max(entity.scale(relativeTo: nil).x, 0.3) let vortexStrength: Float = 2.0 let lifeSpan: Float = 1.0 particle.mainEmitter.vortexStrength = scale * vortexStrength particle.mainEmitter.lifeSpan = Double(scale * lifeSpan) entity.components[ParticleEmitterComponent.self] = particle }
-
18:19 - Anchoring the portal
import SwiftUI import RealityKit struct PortalApp: App { @State private var immersionStyle: ImmersionStyle = .mixed var body: some SwiftUI.Scene { ImmersiveSpace { RealityView { content in let anchor = AnchorEntity(.plane(.vertical, classification: .wall, minimumBounds: [1, 1])) content.add(anchor) anchor.addChild(makePortal()) } } .immersionStyle(selection: $immersionStyle, in: .mixed) } }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。