🥽

visionOSにおけるARKitはiOS版とどう変わったか

2023/12/18に公開

visionOS / Vision Proは現実を拡張するソフトウェア/ハードウェアであり、ARKitはそのキーとなる技術のひとつだと思うのだが、WWDC23の20以上もあるSpatial Computing関連セッションのうち、意外にもARKitに関するものは実は2つしかない。

そのうちのひとつである "Meet ARKit for spatial computing" の内容を、

https://developer.apple.com/videos/play/wwdc2023/10082/

visionOS向けに特化している部分や、新しく追加された部分に絞って整理し、まとめていく。

逆に従来の(iOS向けの)ARKitの解説は割愛する。

iOS版ARKitについて

過去の公式サンプルやGitHubで公開しているARKit-Sampler、拙著「実践ARKit」等を参照してください。

https://booth.pm/ja/items/1038241

Anchor

AnchorとARAnchor

はいはいアンカーね、と既知のものとして流しそうになったが、元からARKitにあったものは ARAnchor クラスであり、

https://developer.apple.com/documentation/arkit/aranchor

この "Anchor" はvisionOSで新たに追加されたプロトコル

https://developer.apple.com/documentation/arkit/anchor

ARAnchorはクラス、Anchorはプロトコル、ということで今回新たにARAnchorからプロトコルを切り出したのだろうか、と一瞬思ったが、まったく別モノのようだ。

ARAnchorはvisionOSでは使えず、Anchorにも準拠していない。

そしてAnchorはiOSでは使えず、visionOS専用。

TrackableAnchor

トラッキング可能なAnchorとして、TrackableAnchorプロトコルがある。

isTrackedプロパティを持ち、これがfalseのときはトラッキングされていないため、そのアンカーで固定した仮想コンテンツを非表示にする必要がある。

When a trackable anchor is not being tracked, you should hide any virtual content that you have anchored with it.

WorldAnchor

WorldAnchorの定義

visionOSにおいてワールドトラッキング時に、「アプリの原点」に対してアンカーを配置したいときに用いるアンカーがWorldAnchor。

TrackableAnchorに準拠しており、アプリの原点に対してアンカーを配置したい位置と向きをイニシャライザのtransform引数で受け取る。

World AnchorとRecenter

以下の画像はアンカーがない仮想コンテンツとアンカーがある仮想コンテンツの違いを可視化したもの。

どちらのキューブも、アプリの起動時にアプリの原点に対して相対的に配置されているが、

左側の青いキューブはWorldAnchorによって固定されておらず、右側の赤いキューブはWorldAnchorによって固定されている。

デバイスが移動しても、両方のキューブは配置された場所に留まる。

デジタルクラウンを長押しして"Recenter"(再中心化)を行うと、アプリの原点が現在地に移動する。

このとき、アンカーで固定されていない青い立方体は、アプリの原点との相対的な配置を維持するために移動し、アンカーで固定されている赤い立方体は、現実世界との相対的な配置を維持したまま留まる。

WorldAnchorの永続化

ワールドトラッキング中に追加したWorldAnchorは、アプリの起動や再起動を問わず自動的に永続化される。

自動永続化がアプリが実現したい体験にとって望ましくない場合は、アンカーを使い終わったら削除すればOK。(削除すれば永続化されることはない)

以下は永続化の詳細な解説。

As the device moves around, ARKit builds a map of your surroundings.
(デバイスが移動すると、ARKitは周囲のマップを構築します。)

When you add WorldAnchors, we insert them into the map and automatically persist them for you.
(WorldAnchorを追加すると、マップに挿入され、自動的に永続化されます。)

Only WorldAnchor identifiers and transforms are persisted.
(WorldAnchorの識別子とトランスフォームのみが永続化されます。)

No other data, such as your virtual content, is included.
(バーチャルコンテンツなど、他のデータは含まれません。)

It is up to you to maintain a mapping of WorldAnchor identifiers to any virtual content that you associate with them.
(WorldAnchorの識別子を、あなたが関連付ける仮想コンテンツにマッピングするのは、あなた次第です。)

Maps are location based, so when you take your device to a new location -- for instance, from home to the office -- the map of your home will be unloaded, and then a different map will be localized for the office.
(マップはロケーションベースなので、デバイスを新しい場所(例えば自宅からオフィス)に持っていくと、自宅のマップがアンロードされ、オフィスでは別のマップがローカライズされます。)

Any anchors that you add at this new location will go into that map.
(新しい場所で追加したアンカーは、すべてそのマップに入ります。)

When you leave the office at the end of the day and head home, the map that ARKit has been building at the office, along with any anchors that you placed there, will be unloaded.
(一日の終わりにオフィスを出て家に帰ると、ARKitがオフィスで作っていたマップと、そこに置いたアンカーはアンロードされます。)

Once again, though, we have been automatically persisting the map along with your anchors.
(しかし、今回もアンカーと一緒にマップを自動的に永続化しました。)

Upon returning home, ARKit will recognize that the location has changed, and we will begin the process of relocalizing by checking for an existing map for this location.
(帰宅すると、ARKitは場所が変わったことを認識し、この場所の既存のマップを確認して再ローカライズするプロセスを開始します。)

If we find one, we will localize with it, and all of the anchors that you previously added at home will become tracked once again.
(もし見つかったら、それを使ってローカライズし、以前自宅で追加したすべてのアンカーが再び追跡できるようになります。)

ImageAnchor

ImageAnchorもTrackableAnchorに準拠するアンカー。ARImageAnchorとほぼ同じで、画像トラッキング時に検出した画像に対して提供されるアンカー。

When an image is detected, ARKit provides you with an ImageAnchor.
(画像が検出されると、ARKit は ImageAnchor を提供します。)

ImageAnchors can be used to place content at known, statically placed images.
(ImageAnchor は、既知の静的な画像にコンテンツを配置するために使用できます。)

For instance, you can display some information about a movie next to a movie poster.
(例えば、映画のポスターの横に、映画に関する情報を表示することができます。)

ImageAnchors are TrackableAnchors that include an estimated scale factor, which indicates how the size of the detected image compares to the physical size that you specified and the ReferenceImage that the anchor corresponds to.
(ImageAnchorはTrackableAnchorで、推定スケールファクターを含んでおり、検出された画像のサイズが、指定した物理的サイズとアンカーが対応するReferenceImageと比較してどのようになるかを示しています。)

HandAnchor

HandAnchorの定義

ハンドトラッキングにより検出された手の情報は、HandAnchorという形で提供される。

HandAnchorはTrackableAnchorであり、skeletonプロパティ(Skeleton型)とchiralityプロパティ(HandAnchor.Chirality型)を持つ。

HandAnchorのtransformは、アプリの原点を基準とした手首のtransformを表す。

A HandAnchor's transform is the wrist's transform relative to the app origin.

HandAnchorsは、手から相対的にコンテンツを配置したり、カスタムジェスチャーを検出するために使用できる。

HandAnchor.Chirality

Chiralityは、左手なのか右手なのかの情報を持つ。

https://developer.apple.com/documentation/arkit/handanchor/chirality

@frozen public enum Chirality : CustomStringConvertible, Sendable {

    case right

    case left

    public var description: String { get }

    public static func == (a: HandAnchor.Chirality, b: HandAnchor.Chirality) -> Bool

    public func hash(into hasher: inout Hasher)

    public var hashValue: Int { get }
}

Skeleton と Joint

Skeleton 型は従来のiOSのARKitでは ARSkeleton3D に相当する、ジョイントにより骨格を表現する構造体。

Joint 構造体は、親ジョイント、名前、親ジョイントに対する相対的な localTransform、ルートジョイントに対する相対的な rootTransform、このジョイントがトラッキングされているかを示す Bool 値をプロパティに持つ。

// Hand tracking

@available(xrOS 1.0, *)
public struct Skeleton : @unchecked Sendable, CustomStringConvertible {

    public func joint(named: SkeletonDefinition.JointName) -> Skeleton.Joint 

    public struct Joint : CustomStringConvertible, @unchecked Sendable {

        public var parentJoint: Skeleton.Joint? { get }

        public var name: String { get }

        public var localTransform: simd_float4x4 { get }

        public var rootTransform: simd_float4x4 { get }

        public var isTracked: Bool { get }
    }
}

手のスケルトンで利用可能なジョイントの一覧

ジョイントは名前で照会することができる。以下の画像は、手のスケルトンで利用可能な全ジョイントを列挙したもの:

  • 手首(.handWrist)は、手のルートジョイント

  • 各指の最初のジョイントは手首の親となる

    • たとえば、1は0の親になる
  • それ以降の指の関節は前の関節の親になる

    • たとえば、2は1の親になる

サンプルコード

HandAnchor を可視化するサンプル:
https://github.com/shu223/visionOS-Sampler/tree/main/04_ARKitHandTracking

PlaneAnchor

平面検出により現実世界から検出された水平面や垂直面は、PlaneAnchorとして提供される。TrackableAnchorではなくAnchorに準拠し、水平 or 垂直を表す Alignment、平面の Geometry、床やテーブルなどの分類を表す Classification 等の情報をプロパティから取得できる。

ARPlaneAnchorとほぼ同じ。

サンプルコードと実装解説

PlaneAnchor を可視化するサンプル:
https://github.com/shu223/visionOS-Sampler/tree/main/02_ARKitPlacingContent

解説記事:
https://zenn.dev/shu223/articles/visionos_planedetection

MeshAnchor

シーンジオメトリは、実世界の形状を表すポリゴンメッシュを含むアンカーを提供する。

このアンカーがMeshAnchorで、こちらはTrackableAnchorではなくAnchorに準拠し、Geometry型のgeometryプロパティを持つ。

MeshAnchor.Geometry構造体は以下のような定義で、SCNGeometryと大体同じかと。

classificationsには以下の種類がある。

メッシュのSwiftでの取り扱いについては以下の記事も参考になると思う:

https://note.com/shu223/n/ncb46b7a0eabe

サンプルコードと実装解説

MeshAnchor を可視化するサンプル:
https://github.com/shu223/visionOS-Sampler/tree/main/03_ARKitSceneReconstruction

解説記事:
https://zenn.dev/shu223/articles/visionos_scenemesh

DeviceAnchor

Apple Vision Proの位置と向きを持つアンカー。

https://developer.apple.com/documentation/arkit/deviceanchor

WorldTrackingProviderqueryDeviceAnchor(atTimestamp:) メソッドを呼ぶことで作成される。

ARKitSession

ARSession との違い

Anchor / ARAnchor と同様に)この「ARKitSession」も従来からある ARSession と一瞬混同するが、まったくの別モノであり、visionOS向けに新規追加されたクラス。

この ARKitSession ではiOSでは使えず、従来からある ARSession はvisionOSでは使えない。

ARKitSession の定義

ARKitSessionrunstop といったメソッドを持ち、概念自体は ARSession と同様にAR体験のセッションを管理するもののようだ。

ただし、ARSession と違い、run メソッドの引数には DataProvider なるものを配列で渡すようになっている。(ARSession では ARConfigurationRunOptions を渡す仕様だった)

DataProvider

DataProvider とは

AnchorARKitSession もAPIこそ新規追加ではあるものの、概念自体はiOS版ARKitから存在するものだった。しかし、この DataProvider に相当するものはiOS版ARKitにはない

以下にWWDC23の"Meet ARKit for spatial computing"でのDataProviderについて触れている部分を引用する:

Once the session is running, the data providers will begin receiving data.
(セッションが実行されると、データ プロバイダーはデータの受信を開始します。)

Updates arrive asynchronously and at different frequencies, depending on the type of data.
(更新は非同期で、データの種類によって異なる頻度で届きます。)

A data provider represents an individual ARKit feature.
(データプロバイダは、個々の ARKit 機能を表します。)

Data providers allow you to poll for or observe data updates, such as anchor changes.
(データ プロバイダを使用すると、アンカーの変更など、データの更新をポーリングまたは観察することができます。)

Different types of data providers provide different types of data.
(データ プロバイダの種類によって、提供されるデータの種類は異なります。)

正直このへんの解説は最初はあまりピンと来なかった。・・・のだが、この後に書くプライバシーの話と、個々の DataProvider について知って「あぁ、そういうやつね」と腹落ちできた(ので、ここでイマイチわかりにくくてもどうか耐えて続きを読んでみてほしい)。

プライバシーと DataProvider

ARKitは多くのカメラやセンサーのデータを利用してAR体験を実現するわけだが、そういったセンサーデータを、クライアント(我々が開発するアプリ)に勝手に提供することはしない。

センサーのデータはARKitのデーモンに送られ、内部アルゴリズムによって処理される。そして、これらのアルゴリズムによって生成された結果としてのデータへアプリがアクセスするには、2つの前提条件がある。

まずひとつは、アプリが Full Space にいること。ARKitは、Shared Spaceにいるアプリにデータを送信しない

もうひとつは、ユーザーからのアクセス許可(permission)を得ていること。


各ARKitデータが必要とするパーミッションのタイプ

このパーミッションが得られない限りは、ARKitはそのタイプのデータを提供しない。

パーミッションの取得には、ARKitSession の requestAuthorization(for:) メソッドを利用する。

// Privacy
// Authorization

session = ARKitSession()

Task {
    let authorizationResult = await session.requestAuthorization(for: [.handTracking])

    for (authorizationType, authorizationStatus) in authorizationResult {
        print("Authorization status for \(authorizationType): \(authorizationStatus)")

        switch authorizationStatus {
        case .allowed:
            // All good!
            break
        case .denied:
            // Need to handle this.
            break
        ...
        }
    }
}

上のサンプルでは、ハンドトラッキングデータへのアクセスを要求している。

If you do not do this, ARKit will automatically prompt the person for permission when you run the session, if necessary.
(これを行わない場合、ARKit はセッションを実行するときに、必要に応じて自動的に相手に許可を求めるプロンプトを表示します。)

とあり、requestAuthorization(for:) を書かない場合でも必要に応じて自動的にプロンプトを出してくれるようだ。

ユーザーがアクセスを拒否しているデータを提供するDataProviderでセッションを実行しようとすると、セッションが失敗する

WorldTrackingProvider

ワールドトラッキングとは

ワールドトラッキング自体はiOS版ARKitから存在する。

ワールドトラッキング中は WorldAnchor を追加することができ、ARKitは、デバイスが移動しても、ユーザーの周囲に相対的に固定されるように更新を行う。つまり WorldAnchors により実空間に仮想コンテンツを設置できるようになる。

(WorldAnchorについては前回の記事でより詳しく解説しています。)

WorldTrackingProvider

このワールドトラッキングを行うためにiOS版ARKitでは ARWorldTrackingConfiguration という ARConfiguration のサブクラスを使用していたのだが、visionOSではWorldTrackingProviderというDataProviderを使用する。

DevicePose

WorldTrackingProviderを使えば、アプリの原点に対するデバイスの姿勢(ポーズ)を取得できる。

デバイスのポーズとは、アプリの原点に対するデバイスの位置と向きのこと。MetalとCompositorServicesを使用して独自のレンダリングを行う場合、このポーズの情報が必要となる。

このポーズを取得するクエリは比較的expensiveであるとのこと。

WWDCセッションではこのポーズの取得とカスタムレンダリングについてコード付きで解説されているが、応用事例となるので本記事では割愛する。

Let's quickly walk through a simplified rendering example to demonstrate how you can provide device poses from ARKit to CompositorServices.
(それでは早速、簡略化したレンダリングの例で、ARKitからCompositorServicesにデバイスのポーズを提供する方法を説明しましょう。)

PlaneDetectionProvider

平面検出で用いるDataProvider。

iOS版ARKitにおいて ARWorldTrackingConfiguration の planeDetection プロパティに horizontal や vertical を指定することに相当。

SceneReconstructionProvider

シーンジオメトリが使用するDataProvider。

iOS版ARKitにおいて ARWorldTrackingConfiguration の sceneReconstruction プロパティに .mesh や .meshWithClassification を指定することに相当。

ImageTrackingProvider

イメージトラッキングで用いるDataProvider。

iOS版ARKitにおいて ARImageTrackingConfiguration を利用すること、あるいは ARWorldTrackingConfiguration 利用時に detectionImages プロパティにリファレンス画像を指定することに相当。

HandTrackingProvider

ハンドトラッキングで用いるDataProvider。

これに相当する機能はiOS版ARKitにはないが、iOSではVisionフレームワークの VNDetectHumanHandPoseRequest を使ってハンドトラッキングを行うことは可能 [1]

iOS版にあってvisionOSにはない機能?

こうして整理してみると、iOS版ARKitにはあった ARConfiguration や、そのプロパティで指定できる機能がいくつか見当たらないことに気付く。

以下の ARConfiguration 派生クラスやプロパティ指定で利用できたiOS版ARKitの機能が、visionOSの DataProvider には見当たらない。

  • ARGeoTrackingConfiguration

    • "Meet Core Location for spatial computing" というセッションがあり、同様のことはCore Locationを用いて実現できそう
  • ARBodyTrackingConfiguration

  • ARFaceTrackingConfiguration

  • ARObjectScanningConfiguration

  • ARConfigurationframeSemantics プロパティ

    • bodyDetection の有効化、personSegmentation の有効化、デプスの取得を指定できる

ボディトラッキングやフェイストラッキングはカメラのフレームデータにアクセスできればVisionフレームワークに食わせてどうにかなりそうではあるが、どうなんだろうか。デプスデータ取得関連も同様。他のセッションやサンプルをみて発見があればまた追記したい。

まとめ

visionOSにおけるARKitを、iOS版ARKitとの違いを中心に紹介し、各 Anchor , ARkitSession, DataProvider についてWWDC / 公式ドキュメントの言及箇所をまとめた。

DataProvider はiOS版ARKitになかった概念だが、こうして列挙してみると、iOS版ではとっ散らかっていたARKitの各種機能の利用方法 [2] を、データの観点から整理しなおしたのだなと個人的には腑に落ちた。

iOSアドベントカレンダー

本記事はiOS Advent Calendar 2023 の18日目の記事です。

https://qiita.com/advent-calendar/2023/ios

脚注
  1. visionOSでもVisionの VNDetectHumanHandPoseRequest というAPI自体は利用できるが、カメラのフレームデータを取得できないので、実質利用不可。 ↩︎

  2. iOS版ARKitでは、DataProvider に相当する機能を利用する場合、ARSession のrun引数に渡す「ARConfiguration の種類を変える」場合や「ARConfiguration プロパティで指定する」場合等、方法が散らばっていた。 ↩︎

Discussion