AR与VR 在光照上最大的不同在于VR 世界是纯数字世界,有一套完整的数学模型,而AR则是将计算机生成的虚拟物体或关于真实物体的非几何信息叠加到真实世界的场景之上实现对真实世界的增强,融合了真实世界与数字世界。就光照而言,VR中的光照完全由开发人员决定,光照效果是一致的,即不会受到运行时其他因素的影响,而AR 中则不得不考虑真实世界的光照与虚拟的3D光照信息的一致性,举个例子,假如在AR 3D应用中设置了一个模拟太阳的高亮度方向光,而用户是在晚上使用这个 AR应用,如果不考虑光照一致性,那么渲染出来的虚拟物体的光照与真实世界其他物体的光照反差将会非常明显,由于人眼对光照信息的高度敏感性,这种渲染可以说是完全失败的,完全没有沉浸感。在AR 中,由于用户与真实世界的联系并未被切断,光照的交互方式也要求更自然,如果真实世界的阴影向左而渲染出来的虚拟物体图影向右,这也是让人难以接受的,所以在 AR 中,必须要能达到虛拟光照与真实光照的一致,虚拟物体渲染出来的阴影应与真实环境中的阴影基本保持一致,这样才能提商虛拟物体的可信度和真实感。
ARKit 支持对用户所处环境光照信息的估计,在 ARConfiguration 类中定义了 isLightEstimationEnabled 布尔属性用于开启和关闭光照估计,该功能默认为启用状态。由于 ARConfiguration 是所有其他配置类的父类,因此 ARKit所有的配置类都支持光照估计功能。
当设置 isLightEstimationEnabled 值为true 时,ARKit 每帧都会对从设备摄像头中采集的图像进行光照估计计算,并且将估计值保存在 frame. lightEstimate 属性中,frame. lightEstimate 是 ARLightEstimate类的实例,ARLightEstimate类只包含两个属性,
在 RealityKit 中,一般情况下开发人员无须关注 frame. lightEstimate 中的值,当设置 isLightEstimationEnabled值为 true 时,RealityKit 会自动使用环境光照估计值渲染虚拟元素的光照。但在使用自定义渲染时,开发人员必须自行处理光照估计。
但在一些情况下,我们也可能需要实时获取当前环境光照估计值,如根据环境光照动态调整特效类型,下面演示如何启用光照估计并获取实时的光照估计值,如代码所示。
//
// LightEstimate.swift
// ARKitDeamo
//
// Created by zhaoquan du on 2024/1/29.
//
import SwiftUI
import ARKit
import RealityKit
import Combine
struct LightEstimate: View {
@State var isFaceTracking = false
var body: some View {
LightEstimateContainer(isFaceTracking: isFaceTracking)
.overlay(content: {
VStack{
Spacer()
Button {
isFaceTracking.toggle()
} label: {
Text( !isFaceTracking ? "人脸追踪光照": "普通光照估计")
.frame(width:150,height:50)
.font(.system(size: 17))
.foregroundColor(.black)
.background(Color.white)
.opacity(0.6)
}
.cornerRadius(10)
Spacer().frame(height: 40)
}
})
.edgesIgnoringSafeArea(.all)
.navigationTitle("光照估计")
}
}
struct LightEstimateContainer: UIViewRepresentable {
var isFaceTracking: Bool = false
init(isFaceTracking: Bool = false) {
self.isFaceTracking = isFaceTracking
}
func makeUIView(context: Context) -> ARView {
let arView = ARView(frame: .zero)
return arView
}
func updateUIView(_ uiView: ARView, context: Context) {
if isFaceTracking {
let config = ARFaceTrackingConfiguration()
config.isLightEstimationEnabled = true
uiView.session.delegate = context.coordinator
context.coordinator.times = 0
uiView.session.run(config, options: [.resetTracking,.removeExistingAnchors])
return
}
let config = ARWorldTrackingConfiguration()
config.planeDetection = .horizontal
config.isLightEstimationEnabled = true
context.coordinator.arView = uiView
uiView.session.delegate = context.coordinator
uiView.session.run(config)
}
func makeCoordinator() -> Coordinator {
Coordinator(parent: self)
}
class Coordinator: NSObject,ARSessionDelegate {
var arView:ARView? = nil
var isPlaced = false
var times = 0
var parent: LightEstimateContainer
init(parent: LightEstimateContainer) {
self.parent = parent
}
func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
guard let anchor = anchors.first as? ARPlaneAnchor,!isPlaced else {
return
}
do {
let planEntity = AnchorEntity(anchor: anchor)
let mesh = MeshResource.generateBox(size: 0.1, cornerRadius: 0.003)
let texture = MaterialParameters.Texture.init(try TextureResource.load(named: "Box_Texture.jpg"))
var meterial = SimpleMaterial(color: .blue,roughness: 0.8 ,isMetallic: false)
meterial.color = .init(tint:.blue,texture:texture)
let modelEntity = ModelEntity(mesh: mesh, materials: [meterial])
planEntity.addChild(modelEntity)
arView?.installGestures(for:modelEntity)
arView?.scene.addAnchor(planEntity)
}catch{
print("无法加载纹理")
}
}
func session(_ session: ARSession, didUpdate frame: ARFrame) {
guard let estimatLight = frame.lightEstimate , times < 10 else {return }
print("light intensity: \(estimatLight.ambientIntensity),light temperature: \(estimatLight.ambientColorTemperature)")
if let estimatLight = frame.lightEstimate as? ARDirectionalLightEstimate {
print("primary light direction: \(estimatLight.primaryLightDirection), primary light intensity: \(estimatLight.primaryLightIntensity)")
}
times += 1
}
}
}
代码中代码逻辑非常清晰,我们使用 session(:didUpdate frame:)代理方法实时地获取每一帧的光照估计值,并打印了光照估计值强度及色温信息。运行代码,在检测到的水平平面上加载木箱物体后,改变真实环境中的光照,可以看到虚拟的木箱光照信息也发生了明显的变化。
除了通用的光照估计,在使用 ARFaceTrackingConfiguration 配置运行 ARSession 时,ARKit 会提供更多关于环境光照的估计信息。在使用 ARFace TrackingConfiguration 配置运行 ARSession,当设置 isLightEstimationEnabled 值为 true 时,ARKit 每帧都会对从设备摄像头中采集的图像进行光照估计计算,并且将估计值保存在 frame. lightEstimate 属性中,但此时 frame. lightEstimate 为 ARDirectionalLightEstimate 类的实例,ARDirectionalLightEstimate类为 ARLightEstimate 的子类,不仅包括 ARLightEstimate 中的属性,还包含另外3个光照估计值属性。
在Reality Kit 中,开发人员亦无须关注这些光照估计值,当设置 isLightEstimationEnabled 值为 true5,Reality Kit就会自动使用环境光照估计值渲染虚拟元素。但在使用自定义谊染时,开发人员必须自己处理光照估计。下面代码是演示在使用 ARFaceTrackingConfiguration 配置时,启用光照估计并获取实时的光照估计值。
func updateUIView(_ uiView: ARView, context: Context) {
let config = ARFaceTrackingConfiguration()
config.isLightEstimationEnabled = true
uiView.session.delegate = context.coordinator
context.coordinator.times = 0
uiView.session.run(config, options: [.resetTracking,.removeExistingAnchors])
return
}
func session(_ session: ARSession, didUpdate frame: ARFrame) {
guard let estimatLight = frame.lightEstimate , times < 10 else {return }
print("light intensity: \(estimatLight.ambientIntensity),light temperature: \(estimatLight.ambientColorTemperature)")
if let estimatLight = frame.lightEstimate as? ARDirectionalLightEstimate {
print("primary light direction: \(estimatLight.primaryLightDirection), primary light intensity: \(estimatLight.primaryLightIntensity)")
}
times += 1
}