ARKit 2 の AREnvironmentProbeAnchor をマニュアル設定で配置する
Beta 版から記事を起こしているため今後変更される可能性あり。
先回、AREnvironmentProbeAnchor を automatic にして自動的に置いたので、今回はマニュアル設定で設置してみる。
どうやら、反射設定を行ったマテリアルの近辺に
複数 AREnvironmentProbeAnchor を設置しただけでは反射が正確になることはないため、
ほとんどの場合は automatic の設定でも良いと思われる。
automatic は初期起動の原点かカメラ位置で AREnvironmentProbeAnchor を設置しているようなので、
それなりの距離を移動しない限り問題ないだろう。
一応、automatic の場合は Probe の有効範囲は無限大になっている。
また、反射画像として生成される画像が荒く、
予想ではカメラからみえる画像から推測して反射画像を作成している模様。
マニュアルで設置が必要となるケースは、AR コンテンツが広範囲のワールドマップ内で動作するものや、 AR の仮想オブジェクトが反射し動く場合だろう。
新規プロジェクトの作成
Xcode 10 で新規プロジェクトの表示 (Command + Shift + N) させ、 Augmented Reality App を選択しプロジェクトを作成する。
宇宙船のマテリアルを変更する
ARKit の新規プロジェクト art.scnassets の ship.scn を開き、ship ノードの子ノードの shipMesh を選択。
Material Inspector (Command + Option + 5) を表示し、 Metalness を 1、Roughness を 0 に設定する。
そのままだと黒いままなので、反射を確認したい場合は Scene Inspector (Command + Option + 7) を表示し、Background を Procedural Sky に変更する。
本来は Environment を変更すると適応されるのだが、Environment のデフォルト設定が「Use Background」になっているため、今回は背景の変更だけでよい。
environmentTexturing の設定をする
viewWillAppear の中にある以下の命令の下に environmentTexturing の設定を入れる。
environmentTexturing を設定することで AREnvironmentProbeAnchor が動作可能になる。
let configuration = ARWorldTrackingConfiguration() configuration.environmentTexturing = .manual
画面をタップで AREnvironmentProbeAnchor を配置する
画面をタップで AREnvironmentProbeAnchor を配置していくのだが、
AREnvironmentProbeAnchor 自体に実体がないため、
一緒に SCNSphere を配置してゆく。
下準備
SCNSphere とマテリアルを設定
SCNSphere をイニシャライズとタップ時の関数で使用するため、ViewController クラス内で使用できるようにするため、IBOutlet の直下に SCNSphere を設定する。
@IBOutlet var sceneView: ARSCNView! let sphereGeo = SCNSphere(radius: 0.06)
次に viewDidLoad の中の "sceneView.scene = scene" の下にマテリアルの設定をする。
// Set the scene to the view sceneView.scene = scene sphereGeo.firstMaterial?.lightingModel = .physicallyBased sphereGeo.firstMaterial?.metalness.contents = NSNumber(value: 1.0) sphereGeo.firstMaterial?.roughness.contents = NSNumber(value: 0.0)
イニシャライズ時にタップの設定をする
先ほど行なったイニシャライズ時のマテリアル設定にタップの処理を入れる。
handleTap という関数を呼ぶようにしている。
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:))) sceneView.addGestureRecognizer(tapGesture)
タップで呼び出される関数
UITapGestureRecognizer からタップで呼び出される handleTap の中身を書く。 と言っても、後ほどつくる setProbeAnchor() を呼び出すだけ。
今回は特に何もないので setProbeAnchor() の中身を handleTap に書き込んでもよい。
@objc func handleTap(_ gestureRecognize: UIGestureRecognizer) { setProbeAnchor() }
下準備完了。
タップ後の処理
setProbeAnchor 関数の中身をつくっていく。 ViewController クラス内に以下のものを書く。
func setProbeAnchor() { let basePosition = simd_float3(0, 0.25, -1.0) if let camera = sceneView.pointOfView { let putPosition = camera.simdConvertPosition(basePosition, to: nil) let position = simd_float4x4( float4( 1, 0, 0, 0), float4( 0, 1, 0, 0), float4( 0, 0, 1, 0), float4(putPosition.x, putPosition.y, putPosition.z, 1) ) let probeAnchor = AREnvironmentProbeAnchor(name: "probe", transform: position, extent: float3(1, 1, 1)) sceneView.session.add(anchor: probeAnchor) let probeNode = SCNNode(geometry: sphereGeo) probeNode.simdTransform = position sceneView.scene.rootNode.addChildNode(probeNode) } }
振る舞いとしてはタップするとカメラ視点から (x:0, y:0.25, z:-1.0) へ AREnvironmentProbeAnchor と SCNShpere が設置される。
setProbeAnchor の流れ
- basePosition に simd_float3 で (x:0, y:0.25, z:-1.0) の初期値を設定する
- sceneView.pointOfView があれば、定数 camera へ渡す
- camera ノードから SCNNode の simdConvertPosition を使用して、現在のカメラ位置と向きから basePosition 方向を足した位置を putPosition に渡す。
- AREnvironmentProbeAnchor では transform のパラメーターが必要であるため simd_float4x4 を使い position に渡す。
- probeAnchor へ、AREnvironmentProbeAnchor を初期化する。ひとまず extent で有効範囲を 1m にしている(設定しない場合は無限大)
- sceneView の ARSession へ設定した probeAnchor を渡し AR 空間に反映させる
- probeNode に SCNSphere のジオメトリを設定
- probeNode に AREnvironmentProbeAnchor 設置時に設定した transform の値を渡し移動して、addChildNode して sceneView に反映させる。
ARKit 2 から移動、回転、拡大縮小が simd フレームワークを使用するようになっており、今回はそちらを使っている。
このレベルのサンプルなら simd を使用しなくても問題はないので、SCNVector3 などを使用して最後に simd に変換してもよいだろう。
今回のコード
まとめ
タップしてゆくと AREnvironmentProbeAnchor を設定することができる。
Scene ファイルにより多くの反射するジオメトリを設置したり、
平面認識後タップでジオメトリ設置と ProbeAnchor の設置を分けて、反射する画像の振る舞いを調べてみるとよいと思われる。