[iOS] Google VR SDK の代替案としての MetalScope

この記事は、iOS で 360° のパノラマ写真・動画を表示する手段として [Google VR SDK for iOS](https://developers.google.com/vr/ios/) を利用する際の問題点と、その代替案としての [MetalScope](https://github.com/ejeinc/MetalScope) を紹介するものです。

MetalScope: Metal-backed 360° panorama view for iOS
https://github.com/ejeinc/MetalScope

ちなみに MetalScope のメンテナは僕です。ダイレクトマーケティング。

# Google VR SDK for iOS の気になるところ

現在公開されている iOS 向け 360° 動画ビュワーの実装としては、[Google VR SDK for iOS](https://developers.google.com/vr/ios/) が最も有名だと思います。その他にもサードパーティのライブラリがいくつか見つかりますが、[Cardboard](https://vr.google.com/cardboard/) 用のステレオ表示でレンズの歪み補正ができるのは上記の Google の SDK 以外にはほとんどありません。

この Google 謹製の SDK は、[ビューを追加するだけで 360° パノラマ写真・動画をネイティブアプリに簡単に埋め込む](https://developers.google.com/vr/ios/vr-view)ことができてとても良い感じなのですが、実際に使ってみると気になる点もあります（そのいくつかは v1.10 以降のアップデートで大幅に改善されました 🎉）。

- Objective-C の API が Swift 向けに最適化されていない
- 視点の回転をリセットできず、また初期値の指定ができない
- ドラッグで視点を回転できるものの、指を離した後に（iOS 特有の）減速アニメーションがなく止まる
- `AVPlayer` が隠蔽されているため動画の細かなコントロールに難あり（[`v1.10`](https://developers.google.com/vr/ios/release-notes#gvr_sdk_and_ndk_for_ios_v1100) と [`v1.20`](https://developers.google.com/vr/ios/release-notes#gvr_sdk_and_ndk_for_ios_v1200) で改善されました）
- 動画再生の挙動が不安定である（[`v1.20`](https://developers.google.com/vr/ios/release-notes#gvr_sdk_and_ndk_for_ios_v1200) で改善されました）
- ステレオ表示時の UI をカスタマイズできない（すごく… Android です…）
- [Bitcode](https://developer.apple.com/library/content/documentation/IDEs/Conceptual/AppDistributionGuide/AppThinning/AppThinning.html) 未対応（[`v1.10`](https://developers.google.com/vr/ios/release-notes#gvr_sdk_and_ndk_for_ios_v1100) で改善されました）
- オープンソースではない（CocoaPods 必須、バグ修正が困難 etc…）

# MetalScope

[MetalScope](https://github.com/ejeinc/MetalScope) は [Google VR SDK for iOS](https://developers.google.com/vr/ios/) を置き換えることを目的に、[SceneKit](https://developer.apple.com/scenekit/) と [Metal](https://developer.apple.com/metal/) をベースに開発した iOS 向けの 360° パノラマ描画フレームワークです。

## Pros & Cons

### Pros
- Swift 3
- 視点回転のリセットが可能（アニメーション付き）
- `AVPlayer` を直接操作可能（MetalScope は出力を受け取るだけ）
- ステレオ表示時の UI を自由にカスタマイズ可能
- SceneKit との互換性（オリジナルの SCNScene を簡単に表示可能）
- オープンソース

### Cons
- Metal 必須（OpenGL ES とは非互換）
- iOS 9.0+
- Objective-C 非サポート
- Cardboard QR コードの読み取りに未対応
- パノラマ表示の最大化とステレオ表示への遷移は手動

## 使い方

### PanoramaView

Google の SDK にある `GVRPanoramaView` と `GVRVideoView` に相当するのが `PanoramaView` です。

```swift
guard let device = MTLCreateSystemDefaultDevice() else {
    fatalError("MetalScope requires Metal 🤘")
}

let panoramaView = PanoramaView(frame: ..., device: device)

// パノラマ画像（平面視）を読み込む
let panoramaImage = UIImage(...)
panoramaView.load(panoramaImage, format: .mono)

// パノラマ動画（立体視）を読み込む
let videoURL = URL(...)
let player = AVPlayer(url: videoURL)
panoramaView.load(player, format: .stereoOverUnder)
player.play()

// 好きな SCNScene を読み込む
panoramaView.scene = ...

// ダブルタップで回転リセット
let recognizer = UITapGestureRecognizer(
  target: panoramaView,
  action: #selector(PanoramaView.setNeedsResetRotation))
recognizer.numberOfTapsRequired = 2
panoramaView.addGestureRecognizer(recognizer)

```

![panorama-preview.gif](https://qiita-image-store.s3.amazonaws.com/0/122609/65b69044-8909-5924-d860-46737bae252d.gif)

60 FPS の動画は[こちら](https://youtu.be/D7wTFA5K96U)

### StereoView + StereoViewController

MetalScope では、ビュー構造の明瞭さを重視して `PanoramaView` とは別に `StereoView` がステレオ表示用のビュークラスとして用意されています。`UIViewController` のサブクラスである `StereoViewController` を使うと interface orientation などの管理を一任することができます。

```swift
let stereoViewController = StereoViewController(device: ...)

// メディアを読み込むか
stereoViewController.load(image, format: .stereoOverUnder)

// SCNScene を PanoramaView から引き継ぎ
stereoViewController.scene = panoramaView.scene

// 必要に応じてステレオの歪み補正の値を調整可能
stereoViewController.stereoParameters = StereoParameters(
  screenModel: .default,
  viewerModel: .cardboardMay2015)

present(stereoViewController, animated: true, completion: nil)
```

![stereo-preview.jpg](https://qiita-image-store.s3.amazonaws.com/0/122609/fd7f3466-4358-b10e-d175-911e3976f64a.jpeg)

# おわりに

[MetalScope](https://github.com/ejeinc/MetalScope) は、もともと弊社アプリ「[VR CRUISE](http://appstore.com/ejeinc/vrcruise)」用に開発した独自実装の 360° 動画プレイヤーを整備・改修して公開したものです（当時は Google VR SDK の iOS 版がまだ公開されていませんでした）。今は Google の SDK も公開され、リリース当初にあった問題点もかなり修正されましたが、一方で気になる点もまだあります。もし必要環境に Metal を許容できる場合には、MetalScope も試してみて下さい 😉