WWDC Quick Look 💓 By SwiftGGTeam
Spatial Preview フレームワークを知る

Spatial Preview フレームワークを知る

元の動画を見る

ハイライト

Spatial Preview フレームワークを使うと、macOS App は visionOS コードを書かずにドキュメントや 3D コンテンツを Vision Pro の Quick Look へ送り、双方向のリアルタイム編集と SharePlay 共同作業を実現できます。

主要内容

「visionOS App をもう一つ書く」から「数行のコードで済む」へ

3D コンテンツツールを作る開発者なら、誰もがこの場面を経験しています。Mac 上で美しい建築モデルやマテリアルを作り、顧客に Vision Pro で没入的に確認してもらいたい。これまでは visionOS のコンパニオン App を別途作り、ネットワーク転送、シーン同期、ジェスチャ操作まで実装する必要がありました。コードベースも、アセットパイプラインも、リリース手順も二重になります。

(00:27) Spatial Preview フレームワークは、この流れを Mac 側の数行のコードにまで縮めます。Mac App が Session を作り、ファイル URL または USD Stage を渡すと、Vision Pro の Quick Look がレンダリングと操作を自動的に引き受けます。visionOS コードを書く必要も、デバイスのペアリングを処理する必要も、独自のネットワーク同期レイヤーを作る必要もありません。

Cinema 4D と SketchUp はすでにこの機能に対応しており、マテリアル変更のリアルタイム双方向同期を実現しています (01:43)。Mac で金属度のパラメータを少し変えると、Vision Pro 内のモデルもすぐに追従します。

2 種類の Session が 2 つの場面をカバーする

フレームワークは 2 種類の Session を提供します。

  • DocumentPreviewSession:空間写真、空間ビデオ、Apple Immersive Video フレーム、PDF などのドキュメント型を扱います。
  • USDPreviewSession:3D コンテンツを扱い、USDKit の USD Stage を使って双方向のリアルタイム編集を行います。

(03:07) どちらを選ぶかはコンテンツの種類で決まります。動画編集ツールなら DocumentPreviewSession、3D モデリングや CAD ツールなら USDPreviewSession を使います。

デバイス検出:Mac Virtual Display が常に接続されていると仮定しない

フレームワークの最初のステップは、対象デバイスのエンドポイントを取得することです。ユーザーが Mac Virtual Display を使っている場合は、接続済みデバイスを直接取得できます。接続していない場合は、同じ iCloud アカウントの Vision Pro をユーザーが手動で選べるデバイスピッカーを App 側で用意する必要があります。

(04:20) Apple は SpatialPreviewDevicePicker という SwiftUI ビューを用意しています。Sheet で表示すれば十分です。ユーザーが常に Mac Virtual Display を有効にしていると仮定してはいけません。そうしないと、ヘッドセットが接続されていないときに「プレビュー」ボタンが何もしないボタンになってしまいます。

自動最適化の両刃の剣

(08:32) Vision Pro の処理能力は高いものの、Mac 側の数百万ポリゴン規模の産業用 USD シーンをレンダリングすると重くなることがあります。Spatial Preview は既定で自動最適化を有効にし、フレームレートを保つためにメッシュ削減とテクスチャのダウンサンプリングを内部で行います。

通常のプレビューには便利ですが、高精度な産業レビューを作る開発者は注意が必要です。メッシュ削減は細部を失わせる可能性があります。.unmodified パラメータを渡せば最適化を無効にできますが、シーンが複雑すぎる場合、start メソッドは assetUnshareable エラーを投げます。つまり、生のアセットをそのままフレームワークへ渡すのではなく、Mac 側でアセット検査や LOD 管理を行う必要があります。

SharePlay 共同作業は最初から組み込まれている

(13:05) Vision Pro 側の SharePlay サポートは組み込みです。複数の共同作業者が同じ Live Session に参加し、同じ空間コンテンツを同時に見たり編集したりできます。1 人が Vision Pro 内で家具の位置を動かすと、全員の表示がすぐに同期されます。

詳細

ドキュメントプレビュー Session を開始する

(03:58) 次のコードは、Document Preview Session を作り、Mac Virtual Display 経由で Vision Pro へ送る方法を示しています。

import SwiftUI
import SpatialPreview

let deviceObserver = ConnectedSpatialEndpointObserver()
let previewSession = DocumentPreviewSession(name: "Immersive.aivu", contentType: .aivu)

func startPreview(contentURL: URL, endpoint: SpatialPreviewEndpoint) async throws {
    let endpoint = try await deviceObserver.endpoint
    try await previewSession.start(endpoint: endpoint)
    try await previewSession.updateContents(url: contentURL)
}

@State var showDevicePicker: Bool = false

var body: some View {
    // ...
    .sheet(isPresented: $showDevicePicker) {
        SpatialPreviewDevicePicker(isPresented: $showDevicePicker) { endpoint in
            showDevicePicker = false
            Task {
                try await startPreview(filename: filename, endpoint: endpoint)
            }
        }
    }
}

重要な点:

  • ConnectedSpatialEndpointObserver() は現在 Mac Virtual Display に接続されているデバイスを監視し、SpatialPreviewEndpoint を返します。
  • DocumentPreviewSession にはコンテンツタイプ(.aivu は Apple Immersive Video を意味します)と Session 名を指定します。
  • start(endpoint:) で Session を開始すると、Vision Pro の Quick Look が自動的に開きます。
  • updateContents(url:) は、開いている Session にコンテンツを送り、同じシーンを再利用します。
  • SpatialPreviewDevicePicker はシステム提供のデバイス選択 UI で、近くにある同じ iCloud アカウントの Vision Pro から選べます。

コンテンツを更新し、Session 状態を監視する

(05:20) ギャラリーで項目を切り替えるときは、毎回新しい Session を作るのではなく、updateContents でシーンを再利用します。

import SwiftUI
import SpatialPreview

ForEach(contentURLs, id: \.self) { url in
    Button {
        Task { try await previewSession?.updateContents(url: url) }
    }
}
.task(id: previewSession.map { ObjectIdentifier($0) }) {
    for await state in Observations({ session.state }) {
        if state.isInvalidated {
            previewSession = nil
            break
        }
    }
}

try await previewSession?.close()

重要な点:

  • updateContents(url:) は現在のシーンを再利用し、Vision Pro 上でシーンがちらつきながら作り直されるのを避けます。
  • ファイルを切り替えるたびに start を呼ぶと新しいシーンが起動し、体験が悪くなります。
  • Observationssession.state を監視します。ユーザーが Vision Pro で Quick Look を閉じると isInvalidated が true になり、Mac 側で UI 状態を片付けられます。
  • Session 終了時に close() を呼ぶと、Vision Pro 上のシーンも自動的に閉じます。

USD のライブプレビューを開始する

(07:36) 3D コンテンツでは USDKit で USD シーンを読み込み、USDPreviewSession で送信します。

import SpatialPreview
import USDKit

let deviceObserver = ConnectedSpatialEndpointObserver()
var usdSession: USDPreviewSession?

func shareStage(to endpoint: SpatialPreviewEndpoint) async throws -> USDPreviewSession {
    let endpoint = try await deviceObserver.endpoint

    let stageURL = Bundle.main.url(forResource: "sampleScene", withExtension: "usdz")
    let stage = try USDStage.open(stageURL)
    usdSession = USDPreviewSession(stage: stage)

    try await usdSession?.start(endpoint: endpoint)
}

重要な点:

  • USDStage.open(stageURL) は USDKit を使ってローカルの USDZ ファイルを読み込みます。
  • USDPreviewSession(stage:) は Stage をプレビュー Session として包みます。
  • start(endpoint:) 後、Vision Pro は 3D コンテンツをボリュームビューで表示し、ユーザーはイマーシブビューへ切り替えられます。
  • カメラ視点やワイヤーフレームのマテリアル上書きなどの機能は Vision Pro 側の Quick Look に組み込まれており、Mac 側で追加コードを書く必要はありません。

自動最適化を無効にし、エラーを処理する

(08:56) 高精度シーンでは自動最適化を無効にします。

import SpatialPreview

let endpoint = try await deviceObserver.endpoint
do {
    try await usdSession.start(endpoint: endpoint, parameters: .unmodified)
} catch USDPreviewSession.Error.assetUnshareable {
    // Asset Unshareable エラーを処理
}

重要な点:

  • .unmodified パラメータはメッシュ削減とテクスチャのダウンサンプリングを無効にし、元の精度を保ちます。
  • シーンの複雑さが Vision Pro の許容量を超えると、startassetUnshareable を投げます。
  • Mac 側でこのエラーを捕捉し、低ポリゴン版を読み込むなどの代替手段を提示する必要があります。

USD Variants でレイアウトを切り替える

(10:10) USD の Variant Sets は、Mac と Vision Pro の間でリアルタイムに同期できます。

# LayoutVariants.usda
#usda 1.0
over "furniture" (
    variantSets = "Layout"
    variants = { string Layout = "LayoutA" }
)
{
    variantSet "Layout" = {
        "LayoutA" {
            // 既定の家具の位置と回転
        }
        "LayoutB" {
            // 家具の Prim を別の位置と回転へ移動
        }
        ...
    }
}
import SpatialPreview
import USDKit

func applyLayoutVariant(named layoutVariantName: String) throws {
    let prim = stage.prim(at: SdfPath("/root/furniture"))
    try prim.variantSets?.setSelection("Layout", variantName: layoutVariantName)
}

重要な点:

  • Variant Sets は USD の標準機構で、同じ Prim 上で異なるデータバリアントを切り替えられます。
  • prim.variantSets?.setSelection でバリアントを切り替えると、Stage の変更は Vision Pro へ自動的に同期されます。
  • Vision Pro の Quick Look メニューからもバリアントを切り替えられ、その変更も Mac へ戻ります。
  • インテリアデザイン App に向いており、「モダン」と「無垢材」などのレイアウトをワンタップで切り替えられます。

Stage の変更と注釈を監視する

(10:49) Vision Pro 側で行われた USD Stage の変更を監視します。

import SpatialPreview
import USDKit

let observerToken: ObservationToken

observerToken = stage.addObserver(for: UsdStage.ObjectsDidChange.self) { notice in
    for path in notice.resyncedPaths {
        let prim = notice.stage.prim(at: path)
        guard prim.isValid else { continue }
        if prim.isAnnotation {
            // 注釈の変更を処理
            break
        }
    }
}

(11:13) 注釈データは特定の Schema に従う必要があります。

AppleTextAnnotation {
    string text
    uniform string author
    uniform string identifier
}

/__documentAnnotationGroup__

重要な点:

  • stage.addObserver(for: UsdStage.ObjectsDidChange.self) は Stage の変更通知を購読します。
  • notice.resyncedPaths には変更されたすべての Prim パスが含まれます。
  • prim.isAnnotation はその Prim が注釈かどうかを判定します。
  • 注釈は __documentAnnotationGroup__ の子 Prim として配置され、AppleTextAnnotation Schema に従っていなければ Vision Pro 上で正しく表示されません。
  • 注釈には text(内容)、author(作者識別子)、identifier(一意な識別子)の 3 つのフィールドがあります。

オブジェクト操作とイベント監視を有効にする

(11:33) Vision Pro ユーザーがジェスチャで 3D オブジェクトを動かせるようにするには、USD にメタデータを設定します。

customData = {
    dictionary apple = {
        bool spatialEditable = 1
    }
}

(12:16) Session オプションを設定し、イベントを監視します。

import SpatialPreview
import USDKit

session.start(endpoint: endpoint, options: [.annotations, .perObjectManipulation, .export])

func listenForEvents(session: USDPreviewSession) async {
    for await event in session.events {
        if case .timeChanged(let time) = event {
            playbackModel.timeCode = time
        } else if case .playbackStateChanged(let isPlaying) = event {
            playbackModel.playbackStateChanged(isPlaying)
        }
    }
}

重要な点:

  • spatialEditable = 1 のメタデータマーカーにより、その Prim は Vision Pro 上でジェスチャによるドラッグ移動に対応します。
  • options: [.annotations, .perObjectManipulation, .export] は注釈、オブジェクト操作、書き出しを有効にします。既定ではすべて有効です。
  • session.events はアニメーション再生時間の変化(timeChanged)や再生状態の変化(playbackStateChanged)などのイベントを提供します。
  • これを使って Mac 側の再生コントロール UI を同期できます。

同期進捗を監視する

(12:38) 大きなファイルを転送するときは進捗を表示します。

import SpatialPreview
import USDKit

@State private var sessionProgress: Double = 0

var body: some View {
    // ...
    .task(id: usdSession.map { ObjectIdentifier($0) }) {
        guard let session = usdSession else { return }
        for await fraction in Observations({ session.progress.fractionCompleted }) {
            sessionProgress = fraction
        }
    }
    .overlay(alignment: .bottom) {
        ProgressView(value: sessionProgress)
            .padding()
    }
}

重要な点:

  • session.progress.fractionCompleted は 0 から 1 までの同期進捗を返します。
  • Observations で進捗変化を監視し、UI をリアルタイムに更新します。
  • 数百 MB の USDZ ファイルを転送するときは、回り続けるインジケータよりも進捗バーのほうがユーザーに安心感を与えます。

重要な発想

1. 既存の Mac 向け 3D ツールに「空間プレビュー」ボタンを追加する

作るもの:既存の Mac 向けモデリングツールやデザインツールのツールバーに「Vision Pro でプレビュー」ボタンを追加します。

作る価値:visionOS コードを書き直す必要はありません。数行の Spatial Preview API だけで、製品に「空間コンピューティング」の具体的な機能を持たせられます。顧客はヘッドセット内でモデルを実物大で確認でき、画面上の 3D ビューより直感的です。

始め方SpatialPreviewUSDKit フレームワークを導入し、ConnectedSpatialEndpointObserver でデバイスを取得し、USDPreviewSession を作って現在開いている USD Stage を渡します。

2. リアルタイム共同作業に対応したインテリアデザインレビュー ツールを作る

作るもの:Mac 側に平面図と家具ライブラリを置き、Vision Pro 側に 3D の部屋を表示します。デザイナーと顧客が同時に確認し、レイアウト調整についてリアルタイムに話し合います。

作る価値:SharePlay サポートは組み込みなので、自分でマルチユーザー同期を実装する必要はありません。USD Variants を使えば複数のレイアウト案を用意し、ワンタップで切り替えて比較できます。

始め方USDPreviewSessionoptions: [.annotations, .perObjectManipulation] で注釈とオブジェクト移動を有効にします。Mac 側では ObjectsDidChange 通知を監視し、Vision Pro 上の注釈や位置変更を自分のプロジェクトデータベースに保存します。

3. 動画編集ツールに「空間フレームプレビュー」を追加する

作るもの:Apple Immersive Video からキーフレームを抽出し、ワンクリックで Vision Pro へ送って立体感を確認します。

作る価値DocumentPreviewSession.aivu タイプは Apple Immersive Video フレームを直接サポートします。動画編集者はフル動画を書き出してヘッドセットへ転送することなく、編集中に空間効果を確認できます。

始め方DocumentPreviewSession(name:contentType: .aivu) で Session を作り、updateContents(url:) を呼んでフレームファイルを送ります。ギャラリービューでは updateContents でシーンを再利用し、高速に切り替えます。

4. USD アセット向けの「空間注釈」ワークフローを作る

作るもの:3D モデルレビュー中に、レビュー担当者が Vision Pro 内でモデルに直接注釈をピン留めし、Mac 側が自動的に収集・整理します。

作る価値:注釈は標準 USD Prim として存在し、独自形式に依存しません。Mac App は authoridentifier フィールドに基づいて注釈を分類し、レビューレポートを生成できます。

始め方:注釈 Prim が AppleTextAnnotation Schema に従い、__documentAnnotationGroup の下に置かれていることを確認します。Mac 側では stage.addObserver(for: UsdStage.ObjectsDidChange.self) で変更を監視し、注釈データを抽出します。

5. Vision Pro へ送る前に Mac 側で性能警告を出す

作るものstart を呼ぶ前に USD Stage のポリゴン数とテクスチャサイズを分析し、「このモデルは Vision Pro 上で性能が低下する可能性があります」とユーザーに提示します。

作る価値:自動最適化を無効にした複雑なシーンは assetUnshareable エラーを直接投げるため、体験が悪くなります。Mac 側で事前に検査すれば、簡略版を読み込む選択肢をユーザーに提示できます。

始め方:USDKit で Stage 内のすべての Mesh Prim を走査して面数を合計し、テクスチャサイズを確認します。しきい値を超えたら、低ポリゴン版を選ぶか自動最適化を有効にするよう促します。

関連 Session

  • USDKit フレームワークを知る — USDKit は Spatial Preview が 3D コンテンツを扱うための基盤です。USD Stage、Prim、Layer の概念を理解することが重要です。
  • RealityKit の新機能 — RealityKit は Vision Pro 上で 3D コンテンツをレンダリングする下位フレームワークで、Spatial Preview の Quick Look 表示も RealityKit の上に構築されています。
  • Reality Composer Pro 3 を知る — Reality Composer Pro 3 は Mac 上で USD シーンを編集し、spatialEditable メタデータを設定できます。これは Spatial Preview のワークフローに直接つながります。
  • visionOS でイマーシブ環境を構築する — visionOS のイマーシブビューとボリュームビューの概念を理解すると、Spatial Preview コンテンツの見せ方を設計しやすくなります。
  • visionOS の新機能 — visionOS 27 の新機能は、Mac Virtual Display や SharePlay など、Spatial Preview の基盤となるサポートを提供します。

コメント

GitHub Issues · utterances