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 件のコメント:
コメントを投稿