MagicLeapのPCFってなんなの？壱の型 [概要編]

この記事は、[Magic Leap Advent Calendar 2020](https://qiita.com/advent-calendar/2020/magicleap) 19日目の記事です！

# TL;DR
・PCFは空間スキャンデータに紐づいた座標起点
・PCFの情報はアプリや端末を再起動しても失われない
・PCFの情報は専用サーバー経由で複数端末間で共有できるので、端末間で同じ位置にコンテンツを表示させられる

# はじめに
日夜MagicLeap開発に頭を悩ませるみなさま、PCFという言葉はご存じでしょうか。
PCFは難解で、取り扱いも難しいものですが、
正しく使えばスマホARとは一味違う空間コンテンツを作ることができます…！

くわしい実装面の解説は20日目の @uniom さんにお任せするとして、
ここではそもそもPCFとは何なのかを説明していこうと思います。

# PCFとは
### PCFって何？
Persistent(永続的な) Coordinate(座標) Frame(枠組み)の略です。
ひとことでいうと、座標を永続化するための仕組みです。
[公式の解説ページ](https://developer.magicleap.com/en-us/learn/guides/content-persistence-fundamentals)

PCFは座標の起点となるアンカーのようなもので、空間スキャンデータに基づいていくつか生成されます。
PCFからの相対座標を指定してコンテンツを配置することで、
実空間に対してコンテンツの位置を決め打ちすることができます。
また、PCFは空間スキャンデータとともに端末の固有領域や専用サーバーに保存されるため、
アプリや起動セッションの枠を超えて利用することができます。

### PCFの何が嬉しいのか①
MagicLeap開発で座標といえば、MagicLeap端末を起動した位置からの相対座標で計算される
ヘッドポーズセッション依存の座標系を主に使うと思います。
たとえばUnityで開発する場合、Unityの仮想的な座標系の数値として表れるのは、
まさにこのヘッドポーズ座標(便宜上こう呼びます)です。

この座標系は手軽なのですが、

1. 長時間にわたって装着していると、だんだん座標系が現実世界に対してずれていく
2. 装着者の動き方や周囲の環境によってトラッキングが失われると、座標系がまるごとリセットされてしまう(=**永続的**でない)
3. 座標系が端末を起動した時の端末の向きに依存してしまうため、起動が儀式化して厄介

などの欠点があります。


PCFを座標の起点として使えば、
あらかじめスキャンしておいた周囲の空間をもとに座標を求めることができるので、

1. 空間をMagicLeapが認識している限り座標が補正され続けるため、精度がかなり良い
2. 端末の再起動を経ても情報が保存されており、トラッキングが失われた時でも復旧が可能
3. 起動時に儀式しなくてよくなる

と、ヘッドポーズ座標系の欠点をカバーできます。

### PCFの何が嬉しいのか②
上でも少し触れましたが、PCFの情報は端末の再起動を経ても保存されます。
PCFの保存先は、空間のスキャンデータの保存設定で変更できるのですが、
・**On Device** (=端末上のみ)
・**Personal World** (=専用サーバーにその端末からしか使えないバックアップを取る)
・**Shared World** (専用サーバーにアップロードして全ユーザーと共有する)
の3種類があり、Shared Worldを使うと多端末でPCFを共有することができます。

多端末間でコンテンツを実空間内で同じ位置に表示したいケースにおいて、
ヘッドポーズ座標系のみでは他端末の座標を特定できず、画像マーカーやARマーカーなどの補助なしには実現できませんが、
PCFを共有すれば、同じ空間にいるだけでコンテンツを共有することが可能になります。
しかも、MagicVerseSDK(MagicLeapのiOS/Android SDK)でもPCFの読み取りがサポートされているので、
MagicLeapから見える世界を、スマホARで同様に見ることも可能になります。

MagicLeap単体での様子 → https://youtu.be/0rmDj6rRiQ0
iPhoneXでの様子→ https://www.youtube.com/watch?v=RJ-sO2HhOCg
iPadProでの様子→ https://www.youtube.com/watch?v=yBI2unB4y8k
MagicLeap複数台での様子(公式YouTubeより)→ https://youtu.be/Fa9Duw5VpAA

# PCFのふるまい
### PCFの種別について
PCFは、永続化のレベルに応じた3つの種別があり、それぞれ
・**Single/Single** (Single-User/Single-Session)
・**Single/Multi** (Single-User/Multi-Session)
・**Multi/Multi** (Multi-User/Multi-Session)
と呼ばれています。

空間スキャンを行うと、そのスキャンデータに基づいてPCFがいくつか生成されます。
この時点ではまだ端末への保存が行われておらず、
**Single/Single**(単一ユーザー/単一起動セッションでのみ使える)という扱いになります。

その後、端末を終了したタイミングなどで端末への保存が行われ、
**Single/Multi**(単一ユーザーだが複数起動セッションに渡って使える)に遷移します。

また、先述のPCFの保存先設定を**Shared World**に設定している場合は、
バックグラウンドでサーバーアップロード処理が走り、
アップロードが完了した時点で**Multi/Multi**(サーバーにアクセスできる他端末もそのPCFを共有して使える)に移行します。

### 実際どんなふうに生成されるの？
MagicLeapのUnity用SDK(UnityPackage)では、PCF Visualizerのサンプルシーンが含まれています。
(他の開発プラットフォームのことは分からないです、ごめんなさい)
サンプルシーンをビルドすると、こんな感じでPCFの配置位置や属性を確認することができます。

https://youtu.be/0rmDj6rRiQ0

CFUID:PCFのユニークID。プログラムで特定のPCFを直接指定する際には、このIDを使う。
Type：先述のPCFの種類。
Confidence:おそらくPCFの正確さのような指標と思われるが、詳しい振る舞いは不明。直訳で「自信」。
ValidRadiusM：PCFからの相対座標の精度を保証する範囲(メートル)？筆者が試した限り、常に1.5と表示される。
RotationErrDeg:PCFの回転方向の精度？筆者が試した限り、常に5.248334と表示される。

## 使っていて困ったこと
### ブラックボックス多め
保存やサーバーアップロード/ダウンロードの処理およびタイミングが完全に隠蔽されていて、
プログラム側から強制取得などができないのが、かなり厳しめです。
取得できているPCFのステータス確認程度なら一応できるのですが、
それもかなりおおまかにしか把握できない印象です。

また、約半年間触っていた中で2-3度PCFサーバーに障害が発生して、
サーバーに上げていたPCFが全く取得できない日があったのですが、
その時もエラーコードなどが返ってこないため、原因特定にかなり時間を要しました。

その時は結局公式Discordに問い合わせたのですが、
世界的に見てもPCFを利用している開発者はまだかなり少ないらしく、
結局MagicLeap社の人が翌日にレスをくれるまで誰からも応答がありませんでした。

### 空間認識のハードウェア的な限界
空間認識に基づいた座標アンカーなだけに、空間スキャンがしづらいシチュエーションにはかなり弱いです。
(※空間スキャンがしづらい状況≒ヘッドポーズトラッキングが切れやすい状況)
暗すぎる場合など、端末の6DoF加速度センサーのみによる簡易座標取得に切り替わることがあり、
そうなってしまうとPCFに紐づけてあってもガンガン座標がずれてしまうのが辛かったです。
他には、部屋を思い切り模様替えするとそれまでと同じ空間として認識されなかったり (当然ではある)、
ごくまれに、部屋の中でも全く同じ形をした別の部分と勘違いして認識してしまう問題などもありました。

# おわりに
PCFは、不完全な部分もあるものの、かなり夢のある技術だと個人的に感じています。

「さっき配置したものが同じ位置に置かれたままになっている」
「MagicLeap内のCGが隣の人と共有できる」
という体験はMagicLeapでいろいろ遊んだ中でもかなり鮮烈で、
今後こうしたデバイスがより広く使われる可能性を、特に強く感じさせてくれるものだと思います。

まだまだ不完全な部分もありますが、まずはサンプルから、ぜひ試してみてほしいと思います！

明日の担当は会社の上司 @uniom さんです！