ARKitサンプルにライトを追加しよう
標準のARKitサンプルで表示される飛行機には影が無く、のっぺりとした印象でリアリティさが欠けてしまっています。
AR空間にライトを追加すると飛行機の凹凸に合わせて影ができて、よりリアルになります。
また、ARKitの特徴としてカメラのビデオキャプチャより周囲の現実世界の明るさを取得してAR空間のライトの強弱を調整することができます。
今回はそのライトの追加方法を紹介します。
修正方針
- ライトをSceneに追加する
- 現在のビデオキャプチャ情報を取得するハンドラを追加する
- 明るさ情報をライトに反映させる
ライトをSceneに追加する
viewDidLoad()に次のコードを追加します。
// Setup Omni Light let light = SCNLight() light.type = .omni let LightNode = SCNNode() LightNode.light = light LightNode.name = "light" LightNode.position = SCNVector3(-1,2,-1) scene.rootNode.addChildNode(LightNode)
ライトの種類は.typeで指定をします。
次のように色々と種類がありますが、特にこだわりが無ければ電球の光を模したOmniライトが良いと思います。
この状態で動作させると飛行機に陰影がついてリアルになります。
現在のビデオキャプチャ情報を取得するハンドラを追加する
ビデオキャプチャ情報は現在のARFrameから取得することができます。
そして、ARFrameはARSessionから取得することができ、ARSessionが動いている状態では絶えず更新がされます。
ではいつARSessionを参照すれば良いのかという疑問が湧いてきますが、それは下記のハンドラが実行された時に取得するのがセオリーです。
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval){ }
上記のメソッドはARSCNViewDelegateのデリゲートメソッドであり、AR空間に何か変化がおきた時に呼ばれます。
当然、光量が変化した時にも呼ばれるためこのタイミングで処理をするのが一番です。
単純に上記コードを追加すればOKです。
明るさ情報をライトに反映させる
現在の明るさ情報はcurrentFrameのlightEstimate プロパティより取得することができます。
lightEstimateには次の2つのプロパティがあり、それぞれ先程追加したlightに設定します。
- ambientIntensity → 光量
- ambientColorTemperature → 光色
具体的には次のように設定をします。
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) { guard let lightEstimate = self.sceneView.session.currentFrame?.lightEstimate else { return } let ambientIntensity = lightEstimate.ambientIntensity let ambientColorTemperature = lightEstimate.ambientColorTemperature if let lightNode = self.sceneView.scene.rootNode.childNode(withName: "light", recursively: true) { guard let light = lightNode.light else { return } light.intensity = ambientIntensity light.temperature = ambientColorTemperature } }
変更後のソースコード
import UIKit import SceneKit import ARKit class ViewController: UIViewController, ARSCNViewDelegate { @IBOutlet var sceneView: ARSCNView! 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")! if let ship = scene.rootNode.childNode(withName: "ship", recursively: true) { ship.isHidden = true } // Setup Omni Light let light = SCNLight() light.type = .omni let LightNode = SCNNode() LightNode.light = light LightNode.name = "light" LightNode.position = SCNVector3(-1,2,-1) scene.rootNode.addChildNode(LightNode) // Set the scene to the view sceneView.scene = scene } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // Create a session configuration let configuration = ARWorldTrackingConfiguration() configuration.planeDetection = [.horizontal] // Run the view's session sceneView.session.run(configuration) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) // Pause the view's session sceneView.session.pause() } // 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 renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { if let ship = sceneView.scene.rootNode.childNode(withName: "ship", recursively: true) { ship.removeFromParentNode() node.addChildNode(ship) ship.isHidden = false let configuration = ARWorldTrackingConfiguration() sceneView.session.pause() sceneView.session.run(configuration) } } func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) { guard let lightEstimate = self.sceneView.session.currentFrame?.lightEstimate else { return } let ambientIntensity = lightEstimate.ambientIntensity let ambientColorTemperature = lightEstimate.ambientColorTemperature if let lightNode = self.sceneView.scene.rootNode.childNode(withName: "light", recursively: true) { guard let light = lightNode.light else { return } light.intensity = ambientIntensity light.temperature = ambientColorTemperature } } 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 } }
0 件のコメント:
コメントを投稿