WWDC Quick Look 💓 By SwiftGGTeam
visionOS で構造化 3D モデルを共同操作する

visionOS で構造化 3D モデルを共同操作する

元の動画を見る

ハイライト

visionOS 27 の RealityKit は ClippingComponent と階層対応の ManipulationComponent を追加します。開発者は Shader や物理エンジンを自作しなくても、Apple Vision Pro 上で 3D モデルの分解、断面表示、自動 exploded view インタラクションを実現できます。

主要内容

「見るだけ」から「分解できる」へ

以前 visionOS でエンジンや建築 BIM のような複雑な 3D モデルを表示する場合、ユーザーは基本的に周囲を歩き回って眺めるだけでした。外装を外して中の部品を見たい場合、開発者はレイキャスト、衝突体、ジェスチャー競合処理を自分で書く必要がありました。断面を切って内部構造を見せたい場合も、Shader を書くか、CSG(構成的ソリッドジオメトリ)でモデルを削る必要がありました。産業ソフトウェアでは標準的なこうした操作が、spatial computing では長く空白のままでした。

Apple は今回、この機能を RealityKit に直接組み込みました。考え方はシンプルです。コンポーネントが Entity ツリーのどこにあるかで、ユーザーが何を操作できるかが決まります。

デモでは、AirPods Pro のモデルが最初はひとつのまとまりとして表示されています。(05:22) 「分解」をタップすると、外装、イヤホン、基板が突然それぞれ独立してつかめる部品になります。ある人が基板を手に取って観察し、別の人が同時にイヤホンを回転させてコネクタを確認できます。同じ SharePlay 空間にいる全員が、完全に同じモデル状態を見ます。

断面表示:内部構造を層ごとに見えるようにする

複雑なモデルでは内部構造が常に難題です。建築内の配管、機器内の基板、エンジン内のピストン。これらは外装に覆われていて、2D 画面では見えず、3D 空間でも手が届きません。

ClippingComponent はこの問題を解決します。(08:16) 軸にそろったバウンディングボックス(AABB)で残す領域を定義し、その外側のジオメトリをレンダラーが毎フレーム破棄します。6つの面はドラッグ可能なインタラクション平面にできます。ユーザーが手を伸ばして引くと、クリッピング平面が動き、内部構造が層ごとに現れます。

これは単純な「一刀切り」ではありません。状態は3段階あります。.off はクリッピングなし、.on は現在のバウンディングボックスでクリッピング、.editing は6つの操作平面を表示してユーザーが範囲を調整できる状態です。

自動 exploded view:どう分解するかをコードが決める

モデルを分解する時、どの軸に沿って展開するのが最適でしょうか。X 軸では部品が固まって見えるかもしれませんし、Y 軸の方が自然かもしれません。以前は開発者がこの判断をハードコードしていました。今は RealityKit が数学で計算します。

このアルゴリズムは「体積加重位置分散」と呼ばれます。(18:16) 各子部品の位置と体積を入力として、3つの軸方向に加重分散を計算し、分散が最も大きい軸を最適な展開方向として選びます。大きな部品が遠くにあるほど、方向選択への影響が強くなります。勘でハードコードするより、ずっと信頼できます。

詳細

アセット準備:階層構造がすべての前提

階層構造のない 3D モデルでは、コードが何を隠すか、何を操作するかを判断できません。(02:57) デモでは2つのエンジンモデルを比較しています。片方は書き出し時にすべてのメッシュがルートノードへ平坦化され、部品名が InteriorPart_01InteriorPart_47 になっていて、コードから対象を探せません。もう片方は深いネスト構造を保っており、各ピストンやクランクシャフトが独立した名前付きノードなので、コードから直接参照できます。

アーティストが USDZ を書き出す時は、階層を必ず保持する必要があります。後続のすべてのインタラクションはそこに依存します。

分解と組み立て:コンポーネントの位置を移動するだけ

ManipulationComponentInputTargetComponent が付いているノードを、ユーザーが操作できます。(07:09)

分解時は、この2つのコンポーネントをルートノードから外し、各子部品に付けます。

func openAssembly() {
    components[ManipulationComponent.self] = nil
    components[InputTargetComponent.self] = nil

    for child in assemblyChildren {
        child.components.set(InputTargetComponent())

        var manipulation = ManipulationComponent()
        manipulation.releaseBehavior = .stay
        child.manipulationComponent = manipulation
    }
}

組み立て時は逆に、子ノードのコンポーネントを消してルートへ戻します。

func closeAssembly() {
    for child in assemblyChildren {
        child.manipulationComponent = nil
        child.components[InputTargetComponent.self] = nil
    }

    components.set(InputTargetComponent())
    var manipulation = ManipulationComponent()
    manipulation.releaseBehavior = .stay
    manipulationComponent = manipulation
}

重要ポイント:

  • releaseBehavior = .stay により、部品は手を離した位置にとどまり、元の位置へ戻りません
  • InputTargetComponent は Entity がジェスチャーイベントを受け取れるようにします。ただし前提として CollisionComponent が必要です
  • 追加の状態フラグや複雑なアニメーション状態機械は不要です。コンポーネントの位置を変えることが、そのまま挙動の変更になります

断面表示:4つの座標系変換とベクトル射影

ClippingComponent の中心となる属性は bounds です。これは Entity のローカル座標系における AABB です。(09:12) この範囲を超えたジオメトリは毎フレーム破棄されます。

var clipping = ClippingComponent()
clipping.bounds = bounds
clipping.shouldClipChildren = true
clipping.shouldClipSelf = true
entity.components.set(clipping)

重要ポイント:

  • bounds は Entity のローカル座標系で定義する必要があります
  • shouldClipChildren の既定値は false です。子ノードは既定ではクリッピングされないため、明示的に true を設定します
  • shouldClipSelf の既定値は true で、Entity 自身のジオメトリをクリップするかを制御します

断面表示の難しさは座標系変換にあります。(11:12) ユーザーがクリッピング平面をドラッグするジェスチャーは Clipping Plane 座標系で発生しますが、ClippingComponentbounds 更新は Model 座標系で行う必要があります。全体の経路には4つの座標系が関わります。

  1. World:世界座標系。すべての基準です
  2. Model:モデル座標系。ClippingComponent が操作する空間です
  3. Clipping Control:6つの操作平面を配置する空間です
  4. Clipping Plane:ジェスチャーイベントが表現される空間です

変換経路は、Clipping Plane のジェスチャー delta -> World -> Model -> 法線方向へ制約 -> bounds 更新、となります。同時に、制約後の delta を Clipping Plane 座標系へ戻し、操作平面の位置も更新します。

制約にはベクトル射影を使います。(15:44) ドラッグ delta をクリッピング平面の法線方向、たとえば +x 軸 {1, 0, 0} へ射影し、その方向成分だけを残します。これにより、ユーザーが斜めにドラッグしてもクリッピング平面は法線方向にだけ動き、自然な操作感になります。

数学的には、まず方向ベクトルを単位ベクトルにし、ドラッグ delta との内積でスカラー長を求め、最後に方向ベクトルを掛けて制約後のベクトルを得ます。

自動 exploded view:体積加重分散で軸を選ぶ

展開方向の選択は、統計学の分散という考え方に基づきます。(19:46) 分散は、値の集合が平均からどれだけ離れているかを表します。分散が大きいほど値は散らばり、小さいほど集中しています。

加重分散では各値に重みを与えます。ここでは各子部品の体積を重みとして、3つの軸方向に「体積加重位置分散」を計算します。

weighted_variance = sum(weight * (position - average)^2) / count

分散が最も大きい軸が展開方向として選ばれます。平均位置から遠く、体積も大きい部品は、方向選択へ強い影響を与えます。AirPods Pro のデモでは Y 軸の分散が最大でした。外装とイヤホンが Y 方向に最も広がり、体積も大きく重みが高かったためです。

方向が決まったら、FromToBy アニメーションで子部品をその軸に沿って展開位置へ移動します。(23:35)

重要ポイント

1. 工業デザインレビュー App

何を作るか:エンジニアリングチームが Apple Vision Pro 内で機械部品を共同レビューできる App。

なぜ価値があるか:ManipulationComponent の階層的な付け替えにより、「全体を見る -> 部品へ分解する -> 個別に議論する -> 組み立て直す」という流れを、ほぼ追加ロジックなしで実現できます。SharePlay により、リモートチームも同じ状態を共有できます。

どう始めるか:階層構造を持つ USDZ モデルを準備し、ルートノードに ManipulationComponent を付け、openAssembly()closeAssembly() を呼ぶ2つのボタンを作ります。

2. 建築 BIM 断面ビューア

何を作るか:建築家や施主が visionOS 内で仮想建築に入り、壁をリアルタイムに断面表示して配管や構造を確認できるビューア。

なぜ価値があるか:ClippingComponent.editing 状態により、非技術者でも直感的にクリッピング平面を調整できます。6つの色付き平面が操作ハンドルになるため、学習コストはほぼありません。

どう始めるか:建築モデルに ClippingComponent を追加し、初期 bounds をモデル全体のサイズに設定します。その後 .editing 状態に入り、ユーザーが6つのクリッピング平面をドラッグできるようにします。

3. 医療機器の 3D 教学ツール

何を作るか:医学部や機器メーカーが、人体器官や医療機器の内部構造を見せるための教学 App。

なぜ価値があるか:自動 exploded view アルゴリズムにより、複雑なモデルでも分解方向が自然になり、モデルごとに手動調整する必要がありません。体積加重により、心臓や肺葉のような大きな器官が展開方向を主導します。

どう始めるか:USDZ モデルを読み込んだ後、各子部品の体積と位置を計算し、体積加重分散アルゴリズムで軸を選び、FromToBy アニメーションで展開します。

4. EC 向け製品分解展示

何を作るか:visionOS 内で購入検討中の電子製品を「分解」し、内部の作りや部品配置を確認できる体験。

なぜ価値があるか:以前はメーカーの宣伝動画でしかできなかった表現を、今はユーザー自身が手で操作できます。releaseBehavior = .stay により部品は空中に留まり、ユーザーは 360 度から詳しく観察できます。

どう始めるか:階層構造を持つ USDZ モデルを使い、分解時に各部品へ ManipulationComponentCollisionComponent を付けます。

5. 都市インフラ可視化

何を作るか:地下管網、地下鉄トンネル、ケーブル配線を見せる都市計画ツール。

なぜ価値があるか:断面表示と exploded view の組み合わせにより、地面の下に埋まった複雑なインフラを初めて直感的に示せます。地上部分は通常表示し、地下部分は地層をクリッピングして管線を直接露出できます。

どう始めるか:地面と地下設備をレイヤー分けしてモデリングし、ClippingComponent で地面レイヤーを切り、地下部分を自動 exploded view で展開します。

関連セッション

  • Spatial Preview フレームワークを知る — Mac App から visionOS 内の 3D モデルをリアルタイムに制御します。デスクトップで編集し、ヘッドセットでプレビューするワークフローに向いています
  • visionOS Object Tracking の強化を探る — 仮想 3D コンテンツを実物体へ重ねます。たとえば実物のレーシングシミュレーターに仮想エンジンモデルを重ね、内部構造を見せられます
  • RealityKit の新機能ManipulationComponentClippingComponent の低レベル API を詳しく解説します
  • Reality Composer Pro 3 — 階層構造を保った USDZ アセットを準備するためのベストプラクティス
  • 近くの人と visionOS 体験を共有する — SharePlay による空間コラボレーションの技術基盤と、複数人で同じ 3D モデル状態を同期する仕組み

コメント

GitHub Issues · utterances