Apple Engine

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

ARKit 1.5 での変更点 (Xcode 9.3 Beta / iOS 11.5 Beta )

Beta なので公開されている情報からのまとめ。
NDA 上スクリーンショットが出せないので今回も文字だけ。

以下の情報に関しては今後変更される可能性があるため注意が必要。

 

主な変更

www.moguravr.com

 

  • 垂直方向平面の認識
  • 画像認識
  • 以前よりも細かな形状認識
  • カメラのオートフォーカス
  • カメラ解像度が 720p から 1080i に変更

 

以下 SDK の変更

「CVarArg, Equatable, Hashable」など Relationships の Conforms To 部分での変更は割愛。

API の和訳だけでは分かりづらいので、次回以降で以下のサンプルの動作を説明予定。

github.com

github.com

 

追加

ARSession

func setWorldOrigin(relativeTransform: matrix_float4x4)

float4x4 の行列を使用し、ARKit のワールド座標の原点を変更する。
デフォルトは端末がアプリを起動した場所。

https://developer.apple.com/documentation/arkit/arsession/2942278-setworldorigin

 

ARSessionObserver

func sessionShouldAttemptRelocalization(ARSession)

ワールドトラッキング中断後に状態回復を試みるかどうかを delegate から問い合わせる。
関数を実装して true を返すと、再開後 ARKit はワールドトラッキング状態を調整しようとするとのこと。

再開できなかった時のためにセッションの run メソッドで resetTracking を設定しトラッキングをやり直せるようにした方がよい。
ちなみに中断から再開する場合、端末がトラッキング失敗した場所の付近で行うと成功しやすいとのこと。

https://developer.apple.com/documentation/arkit/arsessionobserver/2941046-sessionshouldattemptrelocalizati

 

ARWorldTrackingConfiguration

vertical (ARWorldTrackingConfiguration.PlaneDetection)

ARWorldTrackingConfiguration の PlaneDetection でこちらを選択すると垂直方向への検知ができるようになる。
horizontal を共に使用して、垂直と水平の状態を検知することも可能。

https://developer.apple.com/documentation/arkit/arworldtrackingconfiguration.planedetection/2867271-vertical

 

detectionImages

画像認識用で検知するためのリファレンスとなる画像を ARReferenceImage を使用して設定する。
ここで設定した画像がカメラからキャプチャした画像内にあれば ARImageAnchor を置くように設定できる。
リファレンスとなる画像は複数枚設定可能。

https://developer.apple.com/documentation/arkit/arworldtrackingconfiguration/2941063-detectionimages

 

isAutoFocusEnabled

カメラのオートフォーカスを使用するか設定する。
デフォルトで true になっており、オートフォーカスを行う。

https://developer.apple.com/documentation/arkit/arworldtrackingconfiguration/2942262-isautofocusenabled

 

AROrientationTrackingConfiguration

isAutoFocusEnabled

ワールドトラッキング同様にカメラのオートフォーカスを使用するか設定する。

https://developer.apple.com/documentation/arkit/arorientationtrackingconfiguration/2942263-isautofocusenabled

 

ARPlaneAnchor

vertical (ARPlaneAnchor.Alignment)

ARPlaneAnchor の Alignment が vertical の場合、アンカーは重力に対して平行の回転を返す。
また、Alignment からアンカーとして置いた平面が 垂直か水平かどうか調べることができる。
(水平、垂直、互いに重力に対して平行の行列を返すのでコンテンツによっては、垂直または水平で向きを変えなければならなくなる)

https://developer.apple.com/documentation/arkit/arplaneanchor.alignment/2865598-vertical

 

geometry

以前の ARPlaneAnchor は検知した領域は矩形だったが、 geometry はアンカーが検知した平面の形に合わせた多角形のポリゴンの情報を ARPlaneGeometry として提供される。

SceneKit 用にラッパークラスである ARSCNPlaneGeometry が用意されている。

https://developer.apple.com/documentation/arkit/arplaneanchor/2941025-geometry

 

ARPlaneGeometry

アンカーが検知した平面の形に合わせた多角形のポリゴン(三角ポリゴンのメッシュ)として描画する。
SceneKit のカスタムジオメトリ生成する際に必要な値、検知した領域を多角形のポリゴンを囲う境界の頂点を取得できる。

パラメーターは以下のもの。
ほとんどの場合 ARSCNPlaneGeometry を使用すると思われるので詳細は割愛。

  • vertices: [vector_float3]
  • textureCoordinates: [vector_float2]
  • triangleCount: Int
  • triangleIndices: [Int16]
  • boundaryVertices: [vector_float3]

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

 

ARSCNPlaneGeometry

SCNGeometry のサブクラス。
ARPlaneGeometry クラスのメッシュデータの値をラップし、形状を描画する。
OpenGL での使用はできない。

ARSCNPlaneGeometry を使用すると、ARPlaneAnchor の geometry によって提供された平面形状を update(from:) の設定するだけで形状の描画をおこなってくれる。
また、delegate の update 内でジオメトリの設定しないと形状が変わらないので注意。

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

 

ARReferenceImage

画像認識用のリファレンス用の画像を設定し、上記の ARWorldTrackingConfiguration の detectionImages に渡すと動作するようになる。
(リファレンス用の画像の大きさを設定しないとマッチさせる画像の大きさが 0 となりの認識されないので注意)

 

class func referenceImages(inGroupNamed: String, bundle: Bundle?)

inGroupNamed はアセットカタログで作成した「AR Resource Group」の指定、bundle はバンドルの場所を示す。bundle が nil の場合は main bundle。
アセットカタログで設定する際は以下の name と physicalSize は Xcode のインスペクタ上で設定できる。

 

var name: String?

設定した画像の名前。
認識した画像毎に何かしたい場合は名前で判別させる。

 

var physicalSize: CGSize

画像認識した際のワールド空間での大きさを返す。

 

init(CGImage, orientation: CGImagePropertyOrientation, physicalWidth: CGFloat)

アセットカタログを使用せず CGImage からリファレンス用の画像を設定する。

 

init(CVPixelBuffer, orientation: CGImagePropertyOrientation, physicalWidth: CGFloat)

アセットカタログを使用せず、動画などの CVPixelBuffer からリファレンス用の画像を設定する。

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

 

ARImageAnchor

現実世界で画像検索し画像認識後返される画像認識版アンカー。
transform で位置や回転を取得し、referenceImage(ARReferenceImage)の physicalSize で大きさを取得する。

 

var referenceImage: ARReferenceImage

ARReferenceImage として返される。

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

 

ARHitTestResult

static var estimatedVerticalPlane: ARHitTestResult.ResultType (ARHitTestResult.ResultType)

タップなどのヒットテスト時に、垂直面だと予測される位置の結果を返す。

https://developer.apple.com/documentation/arkit/arhittestresult.resulttype/2887455-estimatedverticalplane

 

existingPlaneUsingGeometry (ARHitTestResult.ResultType)

existingPlaneUsingExtent とは異なり予測される大きさと形を返す。

https://developer.apple.com/documentation/arkit/arhittestresult.resulttype/2942264-existingplaneusinggeometry

 

ARCamera.TrackingState.Reason

case relocalizing

ARCamera の TrackingState で何らかの中断後トラッキング再開している状態。

https://developer.apple.com/documentation/arkit/arcamera.trackingstate.reason/2949172-relocalizing

 

追加要素不明

ARError

static func != (lhs: ARError, rhs: ARError) -> Bool

ステータスは追加となっているが何が追加されたのかわからない。

 

変更不明

  • ARSession.RunOptions
  • ARFaceAnchor.BlendShapeLocation
  • ARSCNView / ARSCNViewDelegate
  • ARSKView / ARSKViewDelegate
  • ARCamera / static func != (lhs: ARCamera.TrackingState.Reason, rhs: ARCamera.TrackingState.Reason) -> Bool

 

変更(ドキュメント追記 SDKs Xcode 9.3+)

  • ARError.errorCode
  • ARError.errorUserInfo
  • ARError.localizedDescription
  • ARError.cameraUnauthorized
  • ARError.errorDomain
  • ARError.sensorFailed
  • ARError.sensorUnavailable
  • ARError.unsupportedConfiguration
  • ARError.worldTrackingFailed
  • ARFrame > ARPointCloud > points
  • ARFrame > ARPointCloud > identifiers
  • ARCamera > trackingState

Xcode 9.3 beta.1 での SceneKit の変更

今回 SceneKit の API 的には何も変わっていない。

Hashable プロトコル hashValue に SDK の Xcode 7.1+ が追加されたため、内容が変わっていないものでも Modified(変更)がかかっている様子。

一応、まとめてみた。

 

Added

CVarArg, Equatable, Hashable

  • SCNText
  • SCNShape
  • SCNFloor
  • SCNBox
  • SCNCapsule
  • SCNCone
  • SCNCylinder
  • SCNPlane
  • SCNPyramid
  • SCNSphere
  • SCNTorus
  • SCNTube
  • SCNBillboardConstraint
  • SCNLookAtConstraint
  • SCNDistanceConstraint
  • SCNAvoidOccluderConstraint
  • SCNAccelerationConstraint
  • SCNSliderConstraint
  • SCNReplicatorConstraint
  • SCNIKConstraint
  • SCNTransformConstraint
  • SCNPhysicsSliderJoint
  • SCNPhysicsBallSocketJoint
  • SCNPhysicsConeTwistJoint
  • SCNPhysicsVehicle

 

Modified

  • SCNLayer / CVarArg, Equatable, Hashable, SCNSceneRenderer, SCNTechniqueSupport

SDKs > Xcode 7.1+

  • SCNNode / SCNBoundingVolume / boundingBox
  • SCNNode / SCNBoundingVolume / boundingSphere

変更不明

  • SCNScene.Attribute / SCNView.Option
  • SCNView / SCNView.Option
  • SCNSceneRenderer / SCNHitTestOption
  • SCNSceneRenderer / SCNDebugOptions
  • SCNLight / SCNLight.LightType
  • SCNMaterial / SCNMaterial.LightingModel
  • SCNMaterial / SCNColorMask
  • SCNGeometrySource / SCNGeometrySource.Semantic
  • SCNBillboardConstraint / SCNBillboardAxis
  • SCNPhysicsBody / SCNPhysicsCollisionCategory
  • SCNPhysicsShape / SCNPhysicsShape.Option / SCNPhysicsShape.ShapeType
  • SCNPhysicsWorld / SCNPhysicsWorld.TestOption / SCNPhysicsWorld.TestSearchMode
  • SCNParticleSystem / SCNParticleSystem.ParticleProperty
  • SCNShadable / SCNShaderModifierEntryPoint
  • SCNSceneSource / SCNSceneSource.LoadingOption / SCNSceneSource.AnimationImportPolicy

2018年からの XR (AR/MR/VR) について考える

XR とは Augmented Reality (AR)、Mixed Reality (MR)、Virtual Reality (VR) などの総称である。

ざっくりまとめるとこんな感じ

名称 対応技術
Augmented Reality (AR) ARKit(iOS), ARCore(Android), Vufolia, その他
Mixed Reality (MR) Microsoft HoloLens, Windows Mixed Reality, その他
Virtual Reality (VR) Oculus Rift, HTC Vive, Gear VR, Google VR (Daydream, Cardboard), その他

 

去年からの流れ

VR がいろいろなところで見られるようになったが、やはり機材が高いため体験するためのコストが高く、一般的には VR ZONE などのアトラクション施設や展示会、特定の業務などで使用されるケースがほとんどだった。
また、初期から行なっているインディー系の人たちの VR への熱意は素晴らしい。
ただ、コンテンツのクオリティが上がってきており、制作コストの増加に伴いそれに見合う収益が出るかなど厳しい局面に立たされるのではないかと予想される。

AR に関してはスマートフォンを主軸としたものになるだろうけど、現状では始まったばかりな感じ。
Android の ARCore はまだプレビューで対応機種が少なく、 iOS の ARKit に関しては現状では機能が少なすぎて AR を活かしきるアプリをつくることが難しい。
これら2つに関しては多分今年アップデートされると思われるのでそれを期待したい。

 

今後来る流れ

VR、MR の方は体験できる人が限られるので、ひとまず今回はスマートフォンの AR 方を主軸として考えていく。

 

3DCG の表現や画質の向上

SoC などチップの向上により、更なる表現の向上が可能になると思われる。
また、以下の動画ではリアルタイムでグローバルイルミネーションが実装されている。
NVIDIA の GTX850M で 50fps とのことなので、次期 iPad, iPhone では普通に動かすことが可能な感はある。

 

www.youtube.com

- グローバルイルミネーションの雑な説明 -
現実世界では物体が色を持っているわけではなく、光から特定の色を反射させている。
りんごの赤以外を吸収して赤色を反射しているため、白い紙の上に置くと紙との接地面が若干赤くなる
この光の特性をコンピューターで計算するとかなり時間がかかるためリアルタイムでは難しかった

 

センサーの強化

現状、モバイルではどうなるかわからないが Kinect の強化版が開発されている。
個人的には人間の目を模倣するべきだと思っており SLAM や2眼カメラの技術の進化を期待しているが、モバイル端末に Kinect のようなものが載っても面白い。
Google Tango の後継機が出ないので今後どうなるかは不明だが。

 

shiropen.com

shiropen.com

 

触覚センサーなどの外部センサーやフィードバック

身体的に体感するため、本体とは別デバイスがいくつか登場すると思われる。
現状でも、ARKit と Apple Watch の組み合わせで Apple Watch 側の加速度センサーからの手の動きや心拍計からデータは取れそうではある。

 

shiropen.com

shiropen.com

 

また、現在日本を開発拠点にしているベンチャー VAQSO VR が匂いの出るセンサーを開発している。
Bluetooth で接続され、匂いは1日2時間の使用で1ヶ月使えるとのこと

vaqso.com

 

画像生成

カメラから得られた情報から擬似的に画像や映像を生成することができるのではないかと思っている。
良い例は NVIDIA での画像生成。

shiropen.com

 

画像合成モデルを生成できるのであれば、 解像度に依存しない画像の生成も行うことができるのではないかと思われる。

  

画像認識からの空間共有

画像とセンサー類を使用して位置を合わせることができると思われるので、複数人で空間を共有できると思われる。

Bloomberg 曰く、ARKit 2.0 ではこのような機能が付くのではないか、とのこと。

 

計算や機械学習を使用したモデリングやパターンから生成するプローシージャルモデリング

ゲームでのオープンフィールドなど多量に派生しなければいけないものなど、 ある程度自動的に生成されないと厳しいだろう。 また、ランダムで生成される商品など新しい魅力を提示できるかもしれない。

shiropen.com

 

物体や現実空間への情報提示

Vuforia SDK 4.0 などで現実空間の3次元オブジェクトをマカーにして表示している。

www.youtube.com

 

また、Google Lens や ARKit と Core Location など、 位置情報と周囲の情報の連携が今後強化されるのではないかと思われる。

www.youtube.com

 

AR / VR 間でのスイッチ

スマートフォンが Web やネットワークをつなぐ架け橋となるのなら、 AR は外界とをつなぐものである。

現実空間から仮想空間へモノを渡したり、仮想空間から現実空間へモノを渡したりなど、
もし、外界と仮想空間が行き来できたとしたら面白いだろう。

 

その他

VR に関しては視線を遮ってしまうため、以下のような事故が起こる。 VR でも外界を認識して危険を知らせる必要があるようだ。

www.moguravr.com

 

Xcode 9.2 での SceneKit の変更点

今更だけど、書き忘れていた。

 

Xcode 9.1 から Xcode 9.2 での変更は1つ。

SCNSceneSource.LoadingOption のタイププロパティ useSafeMode が廃止予定となった。

useSafeMode - SCNSceneSource.LoadingOption | Apple Developer Documentation

 

SCNSceneSource.LoadingOption は、ほぼ紹介していなかったと思われるが、
scn などシーンファイルを Y 軸の方向を変更して読み込むなどのオプションを設定できる。

 

今回、廃止予定のタイププロパティ useSafeMode とは?

設定値は Boolean を含む NSNumber で true に設定すると ネットワークなどの外部や扱いに注意が必要なローカルのディレクトリからリソースを読み込む負荷の設定にすることができる。

 

まとめ

廃止になった理由は不明だが、
SCNSceneSource でシーンのリソースを読み込む場合、読み込むリソース自体を開発者が選択しプログラムを書いているため、このプロパティをあまり必要性がないと感じたのかもしれない。

噂されている Snapdragon 845 が Apple A11 Bionic よりも遅い可能性。そして、その先。

来年のハイエンドの Android に搭載される Snapdragon 845 ベンチマークと思われるものが Geekbench に掲載されている。

見たところベンチマーク的に厳しい状況ではある。

チップ コア数 端末 シングルスレッド マルチスレッド
Apple A11 Bionic 6 iPhone 8 4216 10111
Snapdragon 845 8 ? 2422 8351
Samsung Exynos 8895 8 Samsung Galaxy Note 8 1959 6472
Snapdragon 835 8 Samsung Galaxy Note 8 1807 6296

 

また、Apple の次期チップ A12 が現行の 10nm プロセスでの製造から 7nm となると噂されており、 来年もこのペースで Snapdragon は Apple A シリーズに引き離される可能性はある。

iOS のチップを製造している TSMC 社は 2019 年には 5nm プロセスの開発を行い、 3nm の工場も建設している最中だ。
工場も無限にあるわけではないので、多分製造ラインの椅子取りゲームになるとは思われる。

発熱やその他の問題も抱えているが、今後も iOS 端末の発展が約束されていると期待したい。

iMac Pro を買う場合どれを選ぶべきか

現状 Geekbentch 4 でわかる iMac Pro のベンチマークはこのような感じ。

Apple のサイトで紹介されているものはアプリなどの動作やコンパイルに対したベンチなので、Geekbentch 4 とは異なるので注意。たぶん体感としてはあちらの方が正しい

ハード シングルコア シングルコアの差 マルチコア マルチコアの差
10コア iMac Pro 5270 0.92 倍 35412 1.83 倍
8コア iMac Pro 4982 0.88 倍 30240 1.56 倍
12コア Mac Pro 3354 0.59 倍 25816 1.33 倍
4コア iMac 5681 基準値 19353 基準値

 

ハード OpenCL OpenCL の差 Metal Metal の差
iMac Pro Radeon Pro Vega 64 174890 1.44 倍 178059 7.15 倍
Mac Pro FirePro D700 92289 0.76 倍 53347 2.14 倍
iMac Radeon Pro 580 121275 基準値 24897 基準値

 

CPU に関しては Xeon の特性上マルチコアで伸びが大きく値段相応でスペックが上がっている。
Geekbench 4 Compute に関しては Vega 64 の場合、OpenCL と Metal 近い値になっており、 OpenCL と Metal は CPU を GPU を併用し計算するため、 本来あるべき姿のような気はしている。

また、Vega 56 の OpenCL のスコアが 160949 なので、 現状 Vega 56 から Vega 64 変更する場合色々考える必要がある。

 

このベンチマークが正しいのであれば、Mac Pro 12コアと5Kのディスプレイで iMac Pro 8コアと値段の差が少なく、シングルコアで約1.5倍、マルチコアで1.17倍、OpenCLで1.74倍となるため Mac Pro 12コアを購入する必要性がない。
個人的には 10コア が費用対効果が高く、お金があるなら18コアを選ぶべきと考える。

GPU に関しては Windows のベンチマークを見ても Vega 56 と Vega 64 との性能差があまり大きくないため価格を考えると悩みどころではある。

標準構成は Apple では配送が1〜3営業日となっており、家電量販店でももうそろそろ販売しそうなので、早く欲しい場合は8コアを選ぶのもありかも。

 

別案としては、ECC のメモリーや黒い筐体が必要なければ、次の iMac を待ってもよいかもしれない。

もし、10コアの Core i7 や新しい GPU が載ったとしても、そこまで高くならない可能性があるので。

 

ぴーえす

iMac Pro の Xeon W + Vega。
予想ではフルで動かすと電力がそれなりに奪われるので注意。

 

ぴーえす2

新しい Mac Pro については触れていないのは、 多分、高い価格の Xeon 製品が搭載されると思われるので。

ARKit + SceneKit でカメラから取得した映像にエフェクトをかける

ARKit Advent Calendar 2017 | 15日目

 

以下の昨日の記事の動画。

 

前半では ARKit で取得しているカメラ映像に対して Core Image のフィルター CIDotScreen を使用し、 新聞のモノクロ写真のようなモノクロドットや黒の塗りつぶしで構成された効果を適応しており、
こちらのご紹介。

本来は Metal を使用したり、SceneKit の SCNTechnique などを使うべきだが、 今回の処理でも許容範囲かつ実装が簡単なのでこちらを選択した。

(Metal 直で処理した方が速いと思われるのと、負荷がかからないはずなので、頑張れる方は頑張ってほしい)

 

流れ

昨日の記事で紹介しているように ARKit のシーンの背景は、 iOS 端末の背面カメラである iSight or iSight Duo カメラから取得している映像(画像)を渡している。

デバイスから画像を渡しているのだが、この画像は「SCNScene.background.contents」からは そのまま取れないようなので ARKit がフレーム毎に情報を渡している ARFrame から取得する。

そして、ARFrame から取得した画像にフィルターをかけて、シーンにその画像を戻している。

 

ロジックの実行場所

ARKit を使用しているので、ARKit のデリゲートで書くと思いきや、 ARKit がラップしている SceneKit の SCNSceneRendererDelegate で処理をする。

ARKit は AR 関連の処理を行い、その後 SceneKit で表示部分であるレンダリング処理をしており、 レンダリング処理の前にシーンの背景を変えると意図した表示にならない場合があるため。

 

SCNSceneRendererDelegate の renderer メソッド

renderer(_:updateAtTime:) - SCNSceneRendererDelegate | Apple Developer Documentation

 

SCNSceneRendererDelegate

SCNSceneRendererDelegate - SceneKit | Apple Developer Documentation

 

ひとまず試してみる

Xcode から AR のテンプレートを選択して、ViewController に以下のものを書き足す。

func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {

    // ARKit 設定時にカメラからの画像が空で渡されるのでその場合は処理しない
    guard let cuptureImage = sceneView.session.currentFrame?.capturedImage else {
        return
    }
    
    // PixelBuffer を CIImage に変換しフィルターをかける
    let ciImage = CIImage.init(cvPixelBuffer: cuptureImage)
    let filter:CIFilter = CIFilter(name: "CIDotScreen")!
    filter.setValue(ciImage, forKey: kCIInputImageKey)
    
    // CIImage を CGImage に変換して背景に適応
    // カメラ画像はホーム右のランドスケープの状態で画像が渡されるため、CGImagePropertyOrientation(rawValue: 6) でポートレートで正しい向きに表示されるよう変換
    let context = CIContext()
    let result = filter.outputImage!.oriented(CGImagePropertyOrientation(rawValue: 6)!)
    if let cgImage = context.createCGImage(result, from: result.extent) {
        sceneView.scene.background.contents = cgImage
    }
    
}

 

ARSCNView の session.currentFrame.capturedImage で現在のカメラ画像が PixelBuffer 渡されるので、 CIImage で読み取り CGImage に変換して背景画像に渡している。

ちなみに UIImage に変換したら処理の問題か画像が探せない状態になり、背景が 白 or 黒 の状態になった。

 

端末回転時の処理

そのままだと、端末を傾けランドスケープにした際に表示がおかしくなるので、以下の処理を加える。

  1. 端末の向きの状態を保持する変数を用意する
  2. UIVIewController の viewWillAppear で端末の向きがかわった際に NotificationCenter で通知を送る
  3. どの向きの回転を許すか設定する(今回は全ての向き)
  4. NotificationCenter から呼ばれる関数で変数へ傾きを渡す
  5. CGImagePropertyOrientation(rawValue: 6) の 6 を傾きを保持する変数に変える。するとフレーム毎で傾きに合わせて表示が変更される
追記:iPhone 6s で 試しており気づかなかったが、現状 iPhone X では Portrait UpsideDown に使用できない模様。
以下のコードに iPhone X 分岐を追加する必要あり

 

以下 ViewController.swift の全コード。

import UIKit
import SceneKit
import ARKit

class ViewController: UIViewController, ARSCNViewDelegate {

    @IBOutlet var sceneView: ARSCNView!
    
    // 端末の傾きを保持する変数
    var orientationNumber:UInt32 = 6
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Set the view's delegate
        sceneView.delegate = self
        
        // Show statistics such as fps and timing information
        sceneView.showsStatistics = true
        
        // Create a new scene
        let scene = SCNScene(named: "art.scnassets/ship.scn")!
        
        // Set the scene to the view
        sceneView.scene = scene
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        // Create a session configuration
        let configuration = ARWorldTrackingConfiguration()

        // Run the view's session
        sceneView.session.run(configuration)
        
        // 傾きを検知したら通知を送る
        NotificationCenter.default.addObserver(self, selector: #selector(onOrientationChange(notification:)), name: .UIDeviceOrientationDidChange, object: nil)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        
        // Pause the view's session
        sceneView.session.pause()
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Release any cached data, images, etc that aren't in use.
    }

    // MARK: - ARSCNViewDelegate
    
/*
    // Override to create and configure nodes for anchors added to the view's session.
    func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
        let node = SCNNode()
     
        return node
    }
*/
    
    func session(_ session: ARSession, didFailWithError error: Error) {
        // Present an error message to the user
        
    }
    
    func sessionWasInterrupted(_ session: ARSession) {
        // Inform the user that the session has been interrupted, for example, by presenting an overlay
        
    }
    
    func sessionInterruptionEnded(_ session: ARSession) {
        // Reset tracking and/or remove existing anchors if consistent tracking is required
        
    }
    
    func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
        
        // ARKit 設定時にカメラからの画像が空で渡されるのでその場合は処理しない
        guard let cuptureImage = sceneView.session.currentFrame?.capturedImage else {
            return
        }
        
        // PixelBuffer を CIImage に変換しフィルターをかける
        let ciImage = CIImage.init(cvPixelBuffer: cuptureImage)
        let filter:CIFilter = CIFilter(name: "CIDotScreen")!
        filter.setValue(ciImage, forKey: kCIInputImageKey)
        
        // CIImage を CGImage に変換して背景に適応
        let context = CIContext()
        let result = filter.outputImage!.oriented(CGImagePropertyOrientation(rawValue: orientationNumber)!)
        if let cgImage = context.createCGImage(result, from: result.extent) {
            sceneView.scene.background.contents = cgImage
        }
        
    }
    
    // 傾きを全て許可
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return UIInterfaceOrientationMask.all
    }
    
    // 通知の呼び出しから傾きを保持
    @objc func onOrientationChange(notification: NSNotification){
        
        switch UIDevice.current.orientation {
        case .landscapeLeft:
            orientationNumber = 0
        case .landscapeRight:
            orientationNumber = 3
        case .portrait:
            orientationNumber = 6
        case .portraitUpsideDown:
            orientationNumber = 8
        default:
            orientationNumber = 6
        }
        
    }
}

 

今回、全ての向きで回転を許可しており、この場合プロジェクトの TARGETS の General にある Device Orientation の Upside Down にチェックを入れる必要がある。

デフォルトではチェックが入っていないので注意。

f:id:x67x6fx74x6f:20171211171703p:plain

 

なぜこの処理をしようと思ったか。そして発見したこと。

1番の理由は AR と VR の空間をスイッチさせるため。
SceneKit を使用した ARKit ではデバイスカメラ画像の取得 API はなく、ARSCNView のシーンの背景を一度変えると自分で取得する必要がありフィルターの適応はただのおまけ。

そして、背景にエフェクトをかけると部屋や空間が多少汚くてもある程度はごまかせることを発見した。

ひとまず、CIFilter の効果は CIDotScreen 以外にも沢山あるし、頑張ればカスタムのフィルターをつくることができるので、色々試してみるのもよいかと。

 

AR / VR をスイッチするサンプルファイル

github.com

 

ViewController.swift 98行目の

sceneView.scene.background.contents = UIImage(named: "art.scnassets/Background_sky.png")

直していないけど、毎フレームで背景画像を設定しているので、ここはスイッチ後はフラグ立てて1回だけ実行が良いかと。