Apple Engine

Apple, iPhone, iOS, その周辺のことについて

2019 年版 上半期でベストな Mac を考える

Mac Pro が発表され、多分9月以降になると思われるため、
一旦、まとめてみる。
今回も iMac のコスパ最高。

Mac Pro は別記事で書いたためスルー、
あとユーラシア経済連合で未発表のラップトップと思しきものが 7 モデル登録されているため、数ヶ月後ラインナップが変わるかもしれない。

 

3点の注意事項

デスクトップとラップトップで同じコアの CPU でもデスクトップの方が処理能力が高い。 直付の GPU は大体 PCI Express 3.0 x16 128Gbps。
eGPU で使用する Thunderbolt は 40Gbps。
eGPU は同じ GPU でも性能を完全に出しきれない。
販売終了後サポート終了までのカウントダウンが始まるので、長く使いたいなら腹をくくって早めに買うべし。

あと、以降価格は税別価格。

 

コストの安いものを求める

ラップトップ

MacBook Air 13 inch Retina が良い気はする。
MacBook と MacBook Pro 13 inch はスペックが微妙と感じてはいる。

MacBook Pro 13 inch は 4 Core だが、Air 2 Core で GPU スペックは低い。
今自分が使用している MacBook Pro 13 inch Late 2016 と同等のものだと思ってもよい。

Touch Bar はないものの Touch ID はある。

  • 128 GB ¥134,800
  • 256 GB ¥156,800

 

メモリ 16GB ¥22,000。
バッテリー等の問題で Pro 13 inch も 16GB までしかあげられないので問題ないだろう。

ストレージ 256 GB から 512 GB は ¥22,000 なのでわりとお値打ち。

Thunderbolt 3 ポート が 2 つなのでここは困るかもしれない。
必ず電源で1つ奪われ、残りがもう1つしかないので。

 

デスクトップ

Mac Mini

旧 i7 相当の Core i5 6コア ¥122,800。
i7 で +¥22,000。

メモリ、ストレージも Air 同様に ¥22,000 毎で標準スペックの倍になる。

付属品が電源コードしかない。
ポインティングデバイス、キーボード、ディスプレイはないので注意。
あと、intel の内臓 GPU なのでかなりスペックが落ちる。

 

iMac 21.5 inch Retina 4K

Mac mini に 2 〜 4 万足すだけで 4K ディスプレイ、Radeon Pro 555X の GPU、Magic Mouse 2、Magic Keyboard がついてきてお得。

  • 4コア i3 ¥142,800
  • 6コア i5 ¥164,800

 

ただ、標準構成だとストレージが HDD なので、
1TB Fusion Driveストレージ +¥11,000、
256GB SSDストレージ +¥22,000 か
512GB SSDストレージ +¥44,000 にした方が良さげ。

 

ハイスペックなものを考える

iMac Pro CPU 8コア ¥558,800 をベースにしてみる。

  • Intel Xeon W 8コア 3.2GHz / Turbo Boost 4.2GHz
  • 32GB 2,666MHz DDR4 ECC メモリ
  • 1TB SSDストレージ
  • Radeon Pro Vega 56(8GB HBM2メモリ)

 

CPU は 10 Core などグレードをあげると +¥88,000 づつ上がる。

GPU は Vega 64(16GB HBM2) +¥60,500、
Vega 64X(16GB HBM2) +¥77,000。

 

Mac mini

iMac Pro に近い構成で ¥276,800。

  • Intel Core i7 6コア
  • 32GB 2,666MHz DDR4 メモリ
  • Intel UHD Graphics 630
  • 1TB SSDストレージ

 

CPU は 8 コアと比べ 75%、
GPU は非力過ぎるため、eGPU を追加すると 数万〜十数万。

 

MacBook Pro 15 inch

iMac Pro に近い構成で ¥451,300

  • Intel Core i9 8コア
  • 32GB 2,400MHz DDR4 メモリ
  • Radeon Pro Vega 20(4GB HBM2 メモリ)
  • 1TB SSDストレージ

 

第9世代の i9 は Turbo Boost が 5.0GHz であるため、フルパワーだと iMac Pro 8コアの CPU スペックを超える。
(Turbo Boost は本体温度の問題があるのでラップトップでちゃんと動かすのは難しいかも)

GPU は Vega 56 の半分以下なので、アップグレードせず eGPU を追加してもよいと思う。

とりあえず、2018 年版の MacBook Pro 15 inch と値段据え置きで CPU スペックが上がっているのでかなり良い。

 

iMac

iMac Pro に近い構成で ¥468,300

  • Intel Core i9 8コア
  • 32GB 2,666MHz DDR4 メモリ
  • Radeon Pro Vega 48(8GB HBM2 メモリ)
  • 1TB SSDストレージ

 

第9世代の i9 は Turbo Boost が 5.0GHz であるため、フルパワーだと iMac Pro 8コアの CPU スペックを超える。
GPU は 85% ぐらいの性能。

T2 チップがないので、T2 による HEVC エンコードが使えないが GPU 処理でも速く、ファイルサイズが小さくなるのであまり利点はないだろう。

 

eGPU について

Blackmagic eGPU が以下の価格。

  • Blackmagic eGPU ¥89,800
  • Blackmagic eGPU Pro ¥149,000

 

eGPU が Radeon Pro 580、
eGPU Pro が Radeon Pro Vega 56 となっている。

eGPU はグラフィックボード性能をそのまま出せないため、580 や Vega 56 に近しいスペックの eGPU を使用しても、ゲームなどの GPU によるリアルタイム描画はあまり期待できない。

ただ、動画のエンコードや機械学習など複数 GPU 処理を使用できるものに関しては、MacBook Pro や iMac 自体で GPU と eGPU と共に計算を行うので速くはなる。

 

Blackmagic のものは静音性を保つため努力をしているためお高いが、Sonnet や Razer など eGPU のケースとグラフィックボードを別で買う場合かなり安くなる。 Vega 64 の 8GB HBM2 版は安いと5万円以下で買えるのでケースと合わせても 10 万切るし、RX 580 や 590 は更に安い。

 

また、Vega 64 より 1.2 〜 1.3 倍速いの Radeon VII が秋葉館で売られており、Blackmagic eGPU Pro より安い。

www.akibakan.com

 

まとめ

コスト安くしたいなら MacBook Air。
正直、Mac mini や iMac は数万足すと上のグレードが買えるので長く使うならそこら辺も考えた方が良いかも。

ハイスペックでは iMac と MacBook Pro の i9 は、iMac Pro の 8 コアより、シングルスレッドとマルチスレッド共に高いため良い。
約5数万の差なので i9 を薦めるが、iMac と MacBook Pro の i7 もわりとよい。

と書きつつ、Xeon プロセッサの独自機能や GPU が直付されている iMac Pro の方が性能が高く、
eGPU の追加価格がそれなりにするので iMac Pro 購入を検討しても良いかもしれない。

Mac Pro について妄想してみる

なんとなく予想をまとめてみる。
貧民なので今回は基本的にはネガティブな内容。

CTO 価格がわからないが、現状公開されているエントリーモデルの価格から考えるにかなり高い構成になると予想される。
大金を使って少しでも速く処理したいクリエイティブ職向けで、その中でもかなりハイクラスでないとコストに対して見合わない気はする。

とりあえず、エントリーモデルに関しては iMac Pro 8 コアより高く、現状スペックにわりと厳しさがある。
(Radeon Pro 580X がデュアル構成なら若干よいかも)

www.apple.com

 

CPU

CPU が多分 Cascade Lake なので若干良くなるが、それ以外エントリーモデルでは CPU 以外スペックは良くない。
iMac Pro 8 コアから 18 へのアップグレードが ¥264,000 なので、Mac Pro で 28 コアにした場合は確実に 40 万超えそうではある。
(また同スペックの Xeon W のリテール版40万超えるので)

正直、今後 CPU を使いたおすものより、GPU 処理が強いものの方がユーザーとしては利点が多い気はすし、
Apple の紹介ページを見ると 28 コア と iMac Pro 18 コアでは、コア比以上に性能がないので、今後ベーススペックが高かったり、コアの多い Core i 系が出てしまうと iMac Pro と共にコスパがかなり悪いかもしれない。

そのため、前の Mac Pro が 2013 〜 2019 まで販売されていたため、今回優位性が速い段階で無くなる可能性がある。

 

GPU

Apple の紹介ページを見るとデュアル Radeon Pro Vega II と Vega 64X のとの差がギリギリ 2 倍なく、
Cinema 4D リアルタイム 3D レンダリングで Vega 64X と Vega II の差が少ない。
(そもそも、現状複数 GPU を使用してリアルタイム 3D レンダリングを速くする Metal の機能を知らない)

また、PCI Express が行うことができる電力供給の問題で一部スロットが独自の MPX となっており、 Radeon Pro Vege II Duo にした場合、1枚で約 500 W らしいので、CPU GPU を合わせるとスペック通り 1400 W かかるのは間違えないと思われる。

あと、現行 PCI Express が Gen4 が出始め Gen5 が策定されているので、Gen3 である Mac Pro は速い段階で Windows などの PC と GPU 性能差が出るかもしれない。

 

Afterburner

中身の詳細不明。
Apple が FPGA のエンジニアを募集していたとはいえ、すぐに開発はできないと思われるので、Intel Agilex か、Agilex と Apple A チップの混合の気がする。
Agilex の新しいものは最大 40TFlops とのことなので処理はできそう。

 

メモリ

8 コア以外は 2,933MHz メモリに対応している。
12 スロット使えるということは、36TB 以上は可能だと思われる。

多分、今後電源を切ってもデータが失われない不揮発性メモリ Optane DC Persistent Memory 対応できそうではある。
macOS 自体でも調整する必要があるが。

 

電源

  • 1,280W(108〜125Vまたは220〜240V)
  • 1,180W(100〜107V)

Max 出力では 200V 以上必要。
集合住宅などで 100V の場合は単相三線式でないと屋内の工事が必要なるかもしれなく面倒。

 

モジュール式

モジュール式にはなっているが、SSD のスロットは独自らしく、GPU は MPX を使用しているので、他社製で使えるのはメモリと空きの PCI ぐらいかなと。

 

まとめ

正直コスパは悪いが、macOS でハイグレードのものがこれしかないので、有り余るお金がある方には十分な機能をもつハードだと思われる。

ARKit 3 の更新内容(Xcode 11 Beta 2)

Beta 2 では AR の機能変更はない。

 

変更 / 追加

RealityKit の ARView なので厳密には ARKit ではないのだが、
ARView の Scene で Combine の Publisher 関連の関数が増えた。

Combine の Publisher で追加されたものがこちらでも適応されている。

  • publisher(for:)
  • publisher(for:_:)
  • publisher(for:::)
func publisher<T>(for: KeyPath<E, T>) -> Publishers.ValueForKey<Scene.Publisher<E>, T>
func publisher<T0, T1>(for: KeyPath<E, T0>, KeyPath<E, T1>) -> Publishers.ValueForKeys2<Scene.Publisher<E>, T0, T1>
func publisher<T0, T1, T2>(for: KeyPath<E, T0>, KeyPath<E, T1>, KeyPath<E, T2>) -> Publishers.ValueForKeys3<Scene.Publisher<E>, T0, T1, T2>

 

以上

SceneKit の変更点 (Xcode 11 Beta 2)

追加

SCNMaterial、LightingModel で shadowOnly が追加された。
多分、AR などで影だけ表示するためのジオメトリのマテリアル用に追加されたのだろうと思う。

developer.apple.com

 

変更

修飾子 が unowned(unsafe) から weak に変更されたもの。

  • SCNParticlePropertyController の inputOrigin
  • SCNSceneRenderer の delegate

 

以上

SwiftUI でカスタムビューの SceneKit を表示する (Xcode 11 Beta 1)

以下の変更が行われた。
記事のままでは動かず、書き直すのが面倒なので、
リンク先のサンプルコードの ContentView.swift を参照。

 

Beta 4
  • BindableObject が ObservableObject に変更
  • BindableObject が didChange から willChange へ変更
  • List や ForEach で id パラメーターが必須に変更
Beta 5
  • ObservableObject の willChange が @Published のアノテーション記述へ変更

 

サンプルコード

github.com

 


 

基本的には SwiftUI Tutorials で MKMapView を実装しているのと同じ。

環境は Xcode 11.0 Beta 1 を使用しているので、今後動かなくなる可能性があるので注意。

 

今回つくるもの

f:id:x67x6fx74x6f:20190617041655p:plain
完成予想図

SceneKit の SCNView を上部、リストをその下に配置する。
そして、リストをタップすると SceneKit のシーンのカメラポジションを変更される。

ちなみに今回つくるのは iOS 版。
あと、ランドスケープ時の処理を特に入れてないので、横に傾けると描画がおかしくなるので注意。

 

プロジェクトを作成する

Xcode 11.0 の Beta 版を起動し、iOS のテンプレートから「Single View App」を選択し右下の「Next」ボタンを押し、Use SwiftUI のチェックボタンを押してプロジェクトを保存。

ContentView.swift の中身を編集していく。

とりあえず、今回は SceneKit と Combine を使用するので以下を書く。

import SceneKit
import Combine

 

また、今回は ContentView.swift へ全てコードを書いていため、
デバッグも ContentView 部分の記載しかない。

個別でプレビューやデバッグをしたい場合は、個々の View の設定をする。

 

カスタムビューの SCNView をつくる

カスタムビューの概要

カスタムビューを作成する場合は UIViewRepresentable を使用して SwiftUI をの body で使えるカスタムの View にする。
カスタムの View の振る舞いは以下の 4 つ

  • View の値やデリゲートを結びつけるコーディネーター設定
  • View の初期化処理
  • View の更新処理
  • View が破棄される際に行う View やコーディネーターのクリーンナップ処理

 

これに合わせて関数が用意されている。

  • makeCoordinator()
  • makeUIView(context:)
  • updateUIView(_:context:)
  • dismantleUIView(_:coordinator:)

 

今回は makeCoordinator と dismantleUIView 使用しないのでコードを書かないが、makeUIView と updateUIView は設定しないとビルドエラーになるので注意。

 

カスタムビューのコード書く

SceneView という構造体に UIViewRepresentable を準拠させ、makeUIView の関数で SCNView とその中身を書く。

makeUIView の中身はいつも通り SCNView を設定し、シーンをシーンファイルから読み込んでいる。

struct SceneView: UIViewRepresentable {
    
    func makeUIView(context: Context) -> SCNView {
        let view = SCNView(frame: .zero)
        let scene = SCNScene(named: "main.scn")

        view.allowsCameraControl = true
        view.scene = scene

        return view
    }

    func updateUIView(_ view: SCNView, context: Context) {
    }
}

 

起点のビューで先ほど作った SceneView を表示させる

ここでは、アプリ起動の際に最初に実行されるビューコントローラー UIHostingController のビューとなる ContentView の body に SceneView を表示させるよう設定する。

ただ SceneView を設定して高さを設定するだけ。

struct ContentView : View {
    var body: some View {
        
        // SceneKit
        SceneView()
            .frame(height: 300)

    }
}

 

これをプレビューか実行すると中央に SceneKit の内容が描画される。

 

下にリストを表示するコードを書く

はじめに TableView となる List のカスタムセル TableRow 作成する。
固定文字と数値を渡して文字を表示する。

struct TableRow: View {
    var number:Int

    var body: some View {
        VStack(alignment: .leading) {
            Text("Camera: \(number)")
                .font(.title)
            Text("description: !!!!!!!")
                .font(.subheadline)
        }
        .padding(8)
    }
}

 

VStack を使用し、View を縦並びに表示できるようにし、先ほどの SceneView と TableRow を使用した List のコードを書き表示させる。
先ほどの ContentView を以下のように修正する。

struct ContentView : View {
    var body: some View {
        VStack(alignment: .leading) {

            // SceneKit
            SceneView()
                .frame(height: 300)
            
            // TableView
            List(0...2){ i in
                TableRow(number: i)
            }

        }
    }
}

 

これをプレビューか実行すると SceneKit の内容の下にリスト(TableView)が描画される。

 

リストをタップしてシーンのカメラ位置を変更する

今回は Combine フレームワークを使用して BindableObject を作成する。

こちらを使用するとビューの階層に関係無く、値の入出力を紐付け、 どこかで変更が起こった場合はその変化を伝播させる。
今までは通知を使用していたものなどをこちらで代用できる。

ちなみに今回は双方向かつ即更新されるが、一方のみや時間をおいて更新したり、本来は細かい設定ができる。

 

BindableObject を作成する

カメラの位置を Combine を使用し CameraInfo.cameraNumber の UInt 値で保持する。
didChange で値の変更 PassthroughSubject でそのまま渡し、
cameraNumber の didSet で、didChange の send で変更を保存する。

以下、BindableObject で CameraInfo を設定する

final class CameraInfo: BindableObject  {
    let didChange = PassthroughSubject<CameraInfo, Never>()

    var cameraNumber:UInt = 0 {
        didSet {
            didChange.send(self)
        }
    }
}

 

SceneView に値をバインディングさせカメラ位置を変更させるコードを書く

@Binding を使用してカスタムビューに BindableObject と紐づけ、BindableObject の値が変更されるとカメラ位置が変更される。

@Binding のコードを追加して、バインドされた値が変更された時、 updateUIView でカメラ位置を変更するアニメーションを行う。

カメラはシーンファイルに「Camera0」「Camera1」「Camera2」と設定されており、childNode から探し、移動するアニメーションを設定する。

以下、SceneView を変更する。

struct SceneView: UIViewRepresentable {
    @Binding var cameraNumber:UInt

    func makeUIView(context: Context) -> SCNView {
        let view = SCNView(frame: .zero)
        let scene = SCNScene(named: "main.scn")

        view.allowsCameraControl = true
        view.scene = scene

        return view
    }

    func updateUIView(_ view: SCNView, context: Context) {
        let camera = view.scene?.rootNode.childNode(withName: "Camera\( cameraNumber)", recursively: true)!

        SCNTransaction.begin()
        SCNTransaction.animationDuration = 1.0

        view.pointOfView = camera

        SCNTransaction.commit()

    }
}

 

ContentView に BindableObject の設定をする

@EnvironmentObject で BindableObject を使用できるようにして、 SceneView には BindableObject の変更を渡し、List の中身をボタンにする。

それをタップすると BindableObject の中身を変更するようにする。

バインドする際には $ を先頭につけ Binding<UInt> として渡し、リスト内のボタンではアクションのブロックになっているため参照する際には self をつける。

struct ContentView : View {
    @EnvironmentObject var cameraInfo: CameraInfo

    var body: some View {
        VStack(alignment: .leading) {

            // SceneKit
            SceneView(cameraNumber: $cameraInfo.cameraNumber)
                .frame(height: 300)

            // TableView
            List(0...2){ i in
                Button(action: {
                    self.cameraInfo.cameraNumber = UInt(i)
                }) {
                    TableRow(number: i)
                }

            }

        }
    }
}

 

仕上げ

このまま、プレビューや実行をしてもエラーが起きる。

ContentView で「@EnvironmentObject var cameraInfo: CameraInfo」を設定しているため、呼び出す際には EnvironmentObject となる BindableObject (CameraInfo) を設定する必要がある。

SceneDelegate.swift や ContentView.swift の「ContentView()」の箇所を以下のように変更する。

ContentView().environmentObject(CameraInfo())

 

面倒であれば Command + Shift + F でプロジェクト内検索を行い「ContentView()」を「ContentView().environmentObject(CameraInfo())」に書き換えても良いだろう。

 

サンプルコード

github.com

 

まとめ

割と簡単にカスタムのビューの追加とバインディングできたと思われる。

今回は画面が遷移しないので Combine を使用する必要性が薄く、@State で代用できるのだがバインディングが簡単にできるということを示してみた。

ARKit 3 で追加された ARCoachingOverlayView

f:id:x67x6fx74x6f:20190613002222p:plain

iOS 13 から ARKit に ARCoachingOverlayView が実装された。

ARCoachingOverlayView は AR でのセッション初期化中やリミテッドトラッキング状況で、ユーザーに視覚的な指示を表示する UI を提供する。
これまでは自力で実装していたものだが、iOS 13 では頑張ってつくらなくてもよい。

実装的には ARSCNView や ARView に ARCoachingOverlayView を addChild して delegate と各種設定をするだけ。

一応、UIView 継承し、親の View の最上部で表示されるため、ARSCNView や ARView ではなくても使用できる。
ただ、ARKit 用であり ARSession が必要なため ARKit の機能が必要になる。

お約束だが、現状はまだ Beta 版なので今後機能の追加、変更、削除される可能性があるので、その点は注意してほしい。

 

ARCoachingOverlayView のコーチング状態

f:id:x67x6fx74x6f:20190613002247p:plain

初期表示 (Phase 1 〜 3)

アプリを起動すると、ARKit はトラッキングを確立するため役立つコーチングオーバーレイが表示。
ユーザーへデバイスを移動するような表示が出る。

コーチングのゴール表示

平面を見つけるなどのコーチングのゴールを選択していると、 コーチングオーバーレイはそれに従って指示を変更する。

完了

平面認識など目標が達成され、それ以上コーチングが必要とされていないと判断した場合はコーチングオーバーレイが非表示になる。

再検出

ARKit の中断などで再度ワールドトラッキングの状態を回復させるなど、ARKit でリローカライズする必要がある場合、
コーチングオーバーレイはリローカライズを支援するようなユーザーをガイドするコーチングオーバーレイを表示する。

 

ARCoachingOverlayView の実装の流れ

以下を設定すると自動的にオーバーレイの UI が表示される。

  • ARCoachingOverlayView を初期化し、ARSCNView などから session を設定。
  • 平面認識など認識準備や完了で使用するゴールを設定する
  • デリゲートを設定する
  • 表示するビューへ ARCoachingOverlayView を addSubView する

デリゲートでは、ARCoachingOverlayView のアクティブになる前、非アクティブになった時、セッションがリセットされた時の関数が要されている。

 

ARCoachingOverlayView の API

ARCoachingOverlayView

クラスとクラスの使用とイニシャライズ

class ARCoachingOverlayView : UIView

 

goal

検知したい状態をゴールとして設定し、 コーチングオーバーレイがユーザーに指示する表示が決定される。

var goal: ARCoachingOverlayView.Goal { get set }

 

表示で設定できる ARCoachingOverlayView.Goal は以下のもので、 垂直/水平平面の両方、水平平面、トラッキング、垂直平面。

  • anyPlane
  • horizontalPlane
  • tracking
  • verticalPlane
enum Goal : Int

 

session

コーチングを表示するために使用するセッション。
ARSession が更新された場合、このプロパティが使用されセッションからコーチングビューも表示される。

var session: ARSession? { get set }

 

sessionProvider でセッションのデータソースをアクセスできるが、多分直接アクセスすることはあまりないと思う。

@IBOutlet weak var sessionProvider: ARSessionProviding? { get set }

 

delegate

コーチングイベントのコールバックを設定を実装する。

@IBOutlet weak var delegate: ARCoachingOverlayViewDelegate? { get set }

 

ARCoachingOverlayViewDelegate のプロトコルは以下の3つで、
アクティブになる前、非アクティブになった時、セッションがリセットされた時に設定する。

func coachingOverlayViewWillActivate(ARCoachingOverlayView)

func coachingOverlayViewDidDeactivate(ARCoachingOverlayView)

func coachingOverlayViewDidRequestSessionReset(ARCoachingOverlayView)

 

activatesAutomatically

現在のセッション状態に応じて、コーチングビューが自動的にアクティブにするかどうか設定するフラグ。

このフラグ有効になっている場合は、 セッションが初期化されてた時、またはトラッキング条件が特定のしきい値を低下している場合、コーチングオーバーレイがアクティブになり表示される。

デフォルトは true。 false の場合は isActive で調べ、setActive(_:animated:) を使用してマニュアルで非表示にする。

var activatesAutomatically: Bool { get set }

 

isActive

コーチングが行われているかどうかを示すフラグ。
activatesAutomatically が有効な場合、このフラグは自動で変化する。

var isActive: Bool { get set }

 

setActive(_:animated:)

コーチングを行う否かを制御する。

func setActive(_ active: Bool, 
      animated: Bool)

 

サンプルプロジェクトから実装方法を学ぶ

Placing Objects and Handling 3D Interaction のサンプルは以前あったものを ARKit 3 用に若干修正したもの。

ARCoachingOverlayView の実装されているのでこちらを見ながら説明をしてみる。

 

実装概要

このプロジェクトで ARCoachingOverlayView が使用されているのは、ViewController+CoachingOverlay.swift 。

この Swift ファイルは Storyboard で initail View Controller となる ViewController クラスのエクステンションで、ARCoachingOverlayViewDelegate プロトコルを使用し delegete を読み込んでいる。

 

ViewController+CoachingOverlay.swift

このファイルで設定されていないもの
  • coachingOverlay は ARCoachingOverlayView
  • sceneView は ARSCNView
  • upperControlsView は sceneView の上に配置されているボタンの UI(仮想オブジェクトを選択するボタン)
  • restartExperience は別の swift ファイルで実装されている AR のリセット処理

 

初期設定

ARKit をインポートし、ARCoachingOverlayViewDelegate を設定する。

import UIKit
import ARKit

extension ViewController: ARCoachingOverlayViewDelegate {
    ...
}

 

デリゲート

coachingOverlayViewWillActivate は ARCoachingOverlayView がアクティブになる前に実行される。

ARCoachingOverlayView が sceneView に表示されるため、その上にある UI である upperControlsView を非表示にする。

func coachingOverlayViewWillActivate(_ coachingOverlayView: ARCoachingOverlayView) {
    upperControlsView.isHidden = true
}

 

coachingOverlayViewDidDeactivate は ARCoachingOverlayView が非アクティブになった時に実行される。

AR が使用できる状況になるため、使用する UI である upperControlsView を表示する。

func coachingOverlayViewDidDeactivate(_ coachingOverlayView: ARCoachingOverlayView) {
    upperControlsView.isHidden = false
}

 

coachingOverlayViewDidRequestSessionReset はセッションがリセットされた時に実行される。

ここでは restartExperience() が呼ばれ AR の状態がリセットされ、その後 coachingOverlayViewWillActivate が呼ばれる

func coachingOverlayViewDidRequestSessionReset(_ coachingOverlayView: ARCoachingOverlayView) {
    restartExperience()
}

 

メインで呼ばれる関数

setupCoachingOverlay() は ViewController の viewDidLoad() で呼ばれため、この関数はアプリ実行時に呼ばれる。

coachingOverlay.session で sceneView.session を渡し、
coachingOverlay.delegate で ViewController の self を渡す。
その後各関数を呼ぶ。

(わかりやすくするため、元のコードから Auto Layout のコンストレイントの記述は省略している)

func setupCoachingOverlay() {
    coachingOverlay.session = sceneView.session
    coachingOverlay.delegate = self

    setActivatesAutomatically()
    setGoal()
}

 

activatesAutomatically で true に設定している。 デフォルトで true なので明示的に設定している理由は不明。

func setActivatesAutomatically() {
    coachingOverlay.activatesAutomatically = true
}

 

平面認識を行うため goal で .horizontalPlane を指定し、 平面認識を促す UI を表示する様に設定する。

func setGoal() {
    coachingOverlay.goal = .horizontalPlane
}

 

まとめ

そもそも ARCoachingOverlayView クラス自体の機能があまりないため、デリゲートを含めても書く部分は少ない。

認識やトラッキングを行う場合、この様な UI の実装を必ずする必要があるので、iOS 13 からはこちらを使用してみるのも良いかもしれない。

アプリ開発から見る ARKit 3 の新機能

f:id:x67x6fx74x6f:20190612023117p:plain

WWDC 2019 で ARKit 3 が発表された。

ARKit 3 に関する他の記事は Keynote と SwiftStrike を体験した範囲で書かれているものが多いため、アプリ開発側からどのような機能が増えたか公式ドキュメントセッション動画から調べて書いてみた。

 

ARKit 3 で端末制限がある機能 

実は以下の目玉機能が A12 / A12 X Bionic 以降でしか使えない。

  • 仮想オブジェクトで現実空間にいる人を遮蔽するピープル・オクルージョン
  • 人の骨格の動きをトレースするボディトラッキング(モーションキャプチャ)
  • 前面/背面カメラを同時に使用するマルチカム

 

iOS 13 がリリースされる時には、新しい iPhone が発売されるのである程度は増えるが、iOS 13 対応の以下の端末では使用できないこととなる。

  • iPhone X
  • 8/8 Plus
  • 7/7 Plus
  • 6s/6s Plus
  • 7世代目の iPod Touch
  • 3世代目の iPad Pro 以前の iPad

 

別に「A12 / A12 X Bionic 以降でなくても動くのでは?」と思うかもしれないが併用して機能が使用されたり、バッテリー持ちなど Apple の言い分もあると思われ。
まぁ、わからんでもない。

 

ARKit 3 での新機能

  • ピープルオクルージョン
  • ボディトラッキング
  • ARKit でのマルチカムの使用
  • フェイストラッキングの上限増加
  • イメージトラッキングの強化
  • VR 用のトラッキングモード
  • リアルタイムでの HDR テクスチャキューブマップの生成
  • カメラノイズを仮想オブジェクトへの適応
  • レイキャスト
  • AR Coaching UI
  • Multipeer AR Experience(マルチユーザー AR)

 

ピープルオクルージョン

今までの ARKit ではカメラ映像の上に仮想オブジェクトを配置していた。
机の上を認識後、仮想オブジェクトをそこに配置した時、机より前に人がいても仮想オブジェクトが人の前に表示されてしまう。

ARKit 3 からはカメラアプリのポートレートと同じような機能使用し、機械学習で映像から人の部分を切り取り合成し、この問題を解決する。

f:id:x67x6fx74x6f:20190612024028p:plain
左:オクルージョンなし 右:オクルージョンあり

 

iOS SDK 的には、人の部分を切り取るだけのモードと、Z 軸を含んだ奥行き情報(深度情報)を持つ切り取りと2つの機能あり、Metal でカスタムレンダラーを書かずに RealityKit や SceneKit を使用する場合は数行書くだけでこの機能が使用できる。

f:id:x67x6fx74x6f:20190612024606p:plain
深度情報なし

f:id:x67x6fx74x6f:20190612024647p:plain
深度情報あり

 

ボディトラッキング

f:id:x67x6fx74x6f:20190612033410p:plain f:id:x67x6fx74x6f:20190612024755p:plain

バックカメラのカメラ映像から人体を検出し、ARKit で設定されている 93 の骨(スケルトン)を割り当て、仮想キャラクタを動かしたり、モーションキャプチャデータとして使用することができる。
画面座標となる 2D 座標も同時に設定されるため、2次元のキャラにも適応可能。

予想では先のピープルオクルージョンで人体の形状を取得しそこからスケルトンを割り当てているのだと思われる。
(雑に説明すると手や足など画像領域の中間地点が骨の部分になるため、それを繰り返す)

 

iOS SDK 的には、腰骨を中心に各骨が階層構造になっており、3DCG でリグを組んだり触った人は簡単に想像がつくと思われる。
スケルトンの各ボーン(ジョイント)はローカル座標と腰骨からのモデル座標と2つある。

また、既存のワールドトラッキングのラップなので、平面認識やイメージトラッキングを併用することもできる。

 

ARKit でのマルチカムの使用

f:id:x67x6fx74x6f:20190612025145p:plain
セッションでのデモ

iOS 13 前面背面カメラを同時に使用することが可能になったが、ARKit でも使用することができる。

ARKit 3 のセッションでは背面カメラに顔のオブジェクトを置き、前面カメラのフェイストラッキングから動かすデモを行っていた。
例えば、顔や視線でコントロールする AR ゲームなど、使用できる幅は広そう。

この逆のフェイストラッキングで前面カメラ画像を使用している状態からワールドトラッキングを使用することもできる。

iOS SDK でいう ARImageTrackingConfiguration で設定する画像のみのトラッキングでは、この機能を使用することはできない。

 

フェイストラッキングの上限増加

f:id:x67x6fx74x6f:20190612031102p:plain

今までの ARKit はひとりの顔のみの認識可能だったが、iOS 13 から最大3人までの顔を認識できるようになった。

開発的な話だが、顔に対して ID が振り当てられ、途中で1人抜けても ID 保持される。
多分、カメラ画像に対して顔の位置で判別している。

 

イメージトラッキングの強化

一度に最大100枚の画像を検出するようになった。

また、画像自体の物理的なサイズの違いを自動的に判別できるようになった。

 

VR 用のトラッキングモード

VR の様なカメラ画像を使用せず、仮想コンテンツのみで構成する画面用のトラッキング設定が追加された。

カメラ画像は可能な限り荒い解像度を使用し、6DoF で端末が今どこに位置しているかなどの位置推定を行う。

平面認識を使用することは可能。

 

リアルタイムでの HDR テクスチャキューブマップの生成

ARKit 2.0 で機械学習から周囲の画像を推論しキューブマップを作成して、物体の鏡面反射や光を表現をしていた。

ARKit 3.0 ではこれを応用して HDR テクスチャを生成し、光の強さがより現実に近いものになったと思われる。

f:id:x67x6fx74x6f:20190612031739p:plain
右が HDR が適応されているマテリアル

 

カメラノイズを仮想オブジェクトへの適応

iPhone / iPad はハード上仕方ないのだが、カメラで暗い場所を撮ると多くのノイズがのる。
写真では画像調整されるため気付きにくいが、動画を撮ると割とノイズが乗っているのがわかると思う。

そのため、カメラ画像のノイズから推論しそのノイズと同等のものを仮装オブジェクトへ適応するカメラエフェクトが追加され適応できるようになった。

f:id:x67x6fx74x6f:20190612031827p:plain
分かりにくいが、右は仮想オブジェクトにノイズを加えたもの

 

レイキャスト

今までは、画像から推定するヒットテストで画面から仮想オブジェクトへのタッチを検知していたが、光線を飛ばしてぶつかった場所を調べるレイキャストが実装された。

正直、処理が軽いヒットテストでも良いと思っており、RealityKit 側にもヒットテストが実装されているため、導入された理由が次期機能のためにあるのではと思っている。

 

AR Coaching UI

ユーザー側にはあまり関係ない機能だが、 AR を始める際にデバイスを動かして周囲を検知させる状態や、平面認識を促すなどの UI が実装された。

今までは開発者が自前で実装していた。

f:id:x67x6fx74x6f:20190612031154p:plain

 

Multipeer AR Experience(マルチユーザー AR)

同じアプリを起動している複数のデバイスでの空間共有が簡単にできるようになった。
リアルタイムかつ近くにある端末同士の処理なので、2.0 で追加された空間の情報を保存/復元するものとは異なる。

かなり簡単に実装できるため、空間をリアルタイムで共有するゲームや何らかの操作をコラボレーションするアプリが今後増えると思われる。

f:id:x67x6fx74x6f:20190612033806p:plain

 

まとめ

RealityKit への対応、平面認識の高速化、物体認識の精度が上がったりなど他にも機能はあり、かなりの進化を遂げたと思われる。

日本ではオリンピックが開催される流れで日本でも早い段階でマップアプリの Look Around が使える可能性高く、オリンピックに伴い ARKit の機能も強化される可能性もある。

また、今後期待される AR グラスなど、何かあるような期待をさせてくれたのが今回のアップデートのような感じはしている。

 

ARKit で新しく追加された API に関してはこちらを参照。