以下内容由公众号:AIRX社区(国内领先的AI、AR、VR技术学习与交流平台) 整理
光照估计是AR很重要的一个功能,它能让3D虚拟物体在现实环境更具有真实感,ARKit对光照估计有着很好的支持,本部分的教程我们通过几个小案例来了解ARKit的Light特性。
1.在检测到的水平面上放置一个球体节点。
2.添加光照亮球体
3.测试光的强度和温度特性
4.更新和实现ui
5.最后,在SceneKit的场景渲染方法中实现光照估计。
在开始之前先下载基础工程(https://pan.baidu.com/s/1JnBBYytgCtoMj8y9jS8fPw 密码:rj8w),我们在此基础上进行开发(一些UI控件已经准备好)。
首先打开下载好的项目并运行,效果如下图:
在Xcode中打开ViewController.swift文件。在ViewController类中添加以下方法:
func getSphereNode(withPosition position: SCNVector3) -> SCNNode {
let sphere = SCNSphere(radius: 0.1)
let sphereNode = SCNNode(geometry: sphere)
sphereNode.position = position
sphereNode.position.y += Float(sphere.radius) + 1
return sphereNode
}
getSphereNode(withPosition:)方法主要执行以下操作:
1.接受一个位置参数。
2.创建一个半径为0.1 的球体。
3.球体的位置坐标y值为球体的半径。
4.返回球 node。
简而言之,该方法创建一个球体,并将其置于检测到的水平面之上。
接下来,添加一个光源(即SCNLight)来照亮场景。在ViewController类中创建以下方法:
func getLightNode() -> SCNNode {
let light = SCNLight()
light.type = .omni
light.intensity = 0
light.temperature = 0
let lightNode = SCNNode()
lightNode.light = light
lightNode.position = SCNVector3(0,1,0)
return lightNode
}
首先,创建一个SceneKit light对象(即SCNLight),其类型设置为omni。omni(泛光灯类型)从一个点向四面八方照亮一个场景。还有其他类型的光,包括directional(定向光)、spot和ambient(环境光)。
接下来,将light对象的强度和温度属性值设置为零。
将light节点对象的y位置设置为其父节点上方1米的位置。
在ViewController类中添加另一个方法:
func addLightNodeTo(_ node: SCNNode) {
let lightNode = getLightNode()
node.addChildNode(lightNode)
lightNodes.append(lightNode)
}
在renderer(_:didAdd:for:)方法中添加以下内容:
let sphereNode = getSphereNode(withPosition: planeAnchorCenter)
addLightNodeTo(sphereNode)
node.addChildNode(sphereNode)
detectedHorizontalPlane = true
Step 3:测试Light的一些属性
ambientIntensitySliderValueDidChange(_:) 方法:
@IBAction func ambientIntensitySliderValueDidChange(_ sender: UISlider) {
DispatchQueue.main.async {
let ambientIntensity = sender.value
self.ambientIntensityLabel.text = "Ambient Intensity: \(ambientIntensity)"
guard !self.lightEstimationSwitch.isOn else { return }
for lightNode in self.lightNodes {
guard let light = lightNode.light else { continue }
light.intensity = CGFloat(ambientIntensity)
}
}
}
上面的代码运行在主线程上,并将light节点的light intensity属性值设置为slider的sender值。同样,更新ambientColorTemperatureSliderValueDidChange(_:)方法:
@IBAction func ambientColorTemperatureSliderValueDidChange(_ sender: UISlider) {
DispatchQueue.main.async {
let ambientColorTemperature = self.ambientColorTemperatureSlider.value
self.ambientColorTemperatureLabel.text = "Ambient Color Temperature: \(ambientColorTemperature)"
guard !self.lightEstimationSwitch.isOn else { return }
for lightNode in self.lightNodes {
guard let light = lightNode.light else { continue }
light.temperature = CGFloat(ambientColorTemperature)
}
}
}
通过slider控件的值来动态设置Light的temperature属性。测试效果如下:
更新detectedHorizontalPlane属性的didSet方法来动态显示slider控件:
var detectedHorizontalPlane = false {
didSet {
DispatchQueue.main.async {
self.mainStackView.isHidden = !self.detectedHorizontalPlane
self.instructionLabel.isHidden = self.detectedHorizontalPlane
self.lightEstimationStackView.isHidden = !self.detectedHorizontalPlane
}
}
}
在lightEstimationSwitchValueDidChange(_:)方法中添加以下内容:
ambientIntensitySliderValueDidChange(ambientIntensitySlider)
ambientColorTemperatureSliderValueDidChange(ambientColorTemperatureSlider)
剩下的是光估计的实现。对于为什么要光估计?正如在本教程一开始提到的,光估计更进一步增强了3D物体与AR现实世界的融合。例如,如果将房间的灯光调暗,你肯定希望将灯光条件反射到虚拟对象上,使其更真实。所以在ViewController类中添加以下方法:
func updateLightNodesLightEstimation() {
DispatchQueue.main.async {
guard self.lightEstimationSwitch.isOn,
let lightEstimate = self.sceneView.session.currentFrame?.lightEstimate
else { return }
let ambientIntensity = lightEstimate.ambientIntensity
let ambientColorTemperature = lightEstimate.ambientColorTemperature
for lightNode in self.lightNodes {
guard let light = lightNode.light else { continue }
light.intensity = ambientIntensity
light.temperature = ambientColorTemperature
}
}
}
接下来,在renderer(_:updateAtTime:)方法中调用以下方法:
updateLightNodesLightEstimation()
接下来测试完整项目,效果如下图:
你还可以通过开/关你的灯来尝试光的估计。参考效果如下图:
完整项目链接:
https://github.com/appcoda/ARKitLightEstimationDemo
参考资料:
https://www.appcoda.com/arkit-light-estimation/
关于更多机器学习、人工智能、增强现实资源和技术干货,可以关注公众号:AIRX社区,共同学习,一起进步!