如何使用 ARKit 跟踪的水平和垂直平面将对象隐藏在墙壁后面/真实对象后面?目前,当您离开房间和/或在它们应该在它们后面的对象前面时,可以通过墙壁看到添加的 3D 对象。那么是否可以使用 ARKit 提供给我的数据来提供更自然的 AR 体验,而不会让物体穿过墙壁出现?
5 回答
你这里有两个问题。
(而且你甚至没有使用正则表达式!)
如何为 ARKit/SceneKit 创建遮挡几何?
如果将 SceneKit 材质设置colorBufferWriteMask
为空值([]
在 Swift 中),使用该材质的任何对象都不会出现在视图中,但它们仍会在渲染期间写入 z 缓冲区,这会影响其他对象的渲染。实际上,你会得到一个形状像你的对象的“洞”,通过它可以显示背景(在 的情况下是摄像头馈送ARSCNView
),但它仍然可以遮挡其他 SceneKit 对象。
您还需要确保被遮挡的渲染在它应该遮挡的任何其他节点之前。您可以使用节点层次结构来做到这一点(我不记得父节点是在其子节点之前呈现还是相反,但它很容易测试)。层次结构中的对等节点没有确定性顺序,但您可以通过renderingOrder
属性强制一个顺序,而不管层次结构如何。该属性默认为零,因此将其设置为 -1 将在所有内容之前呈现。(或者为了更好地控制,将renderingOrder
多个节点的 s 设置为一系列值。)
如何检测墙壁/等,以便您知道在哪里放置遮挡几何?
在 iOS 11.3 及更高版本(又名“ARKit 1.5”)中,您可以打开vertical
平面检测。(请注意,当您从中获取vertical
平面锚点时,它们会自动旋转。因此,如果您将模型附加到锚点,它们的本地“向上”方向与平面垂直。)iOS 11.3 中的另一个新功能是,您可以获得每个检测到的平面的更详细的形状估计(请参阅ARSCNPlaneGeometry),无论其方向如何。
但是,即使您有水平和垂直,平面的外部限制也只是随时间变化的估计。也就是说,ARKit 可以快速检测墙壁的一部分在哪里,但它不知道墙壁的边缘在哪里,而无需用户花一些时间挥动设备来绘制空间。即便如此,映射的边缘可能不会与真实墙壁的边缘精确对齐。
所以......如果您使用检测到的垂直平面来遮挡虚拟几何体,您可能会发现应该隐藏的虚拟对象显示出来的地方,或者不是完全隐藏在墙的边缘,或者通过一些地方可见ARKit 没有映射整个真实的墙壁。(后一个问题你可以通过假设比 ARKit 更大的范围来解决。)
为了创建一个遮挡材质,它真的很简单
let boxGeometry = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0)
// Define a occlusion material
let occlusionMaterial = SCNMaterial()
occlusionMaterial.colorBufferWriteMask = []
boxGeometry.materials = [occlusionMaterial]
self.box = SCNNode(geometry: boxGeometry)
// Set rendering order to present this box in front of the other models
self.box.renderingOrder = -1
要创建遮挡材料(也称为黑洞材料或阻挡材料),您必须使用以下实例属性:.colorBufferWriteMask
、.readsFromDepthBuffer
和。.writesToDepthBuffer
.renderingOrder
你可以这样使用它们:
plane.geometry?.firstMaterial?.isDoubleSided = true
plane.geometry?.firstMaterial?.colorBufferWriteMask = .alpha
plane.geometry?.firstMaterial?.writesToDepthBuffer = true
plane.geometry?.firstMaterial?.readsFromDepthBuffer = true
plane.renderingOrder = -100
...或者这样:
func occlusion() -> SCNMaterial {
let occlusionMaterial = SCNMaterial()
occlusionMaterial.isDoubleSided = true
occlusionMaterial.colorBufferWriteMask = []
occlusionMaterial.readsFromDepthBuffer = true
occlusionMaterial.writesToDepthBuffer = true
return occlusionMaterial
}
plane.geometry?.firstMaterial = occlusion()
plane.renderingOrder = -100
ARKit 4 和激光雷达扫描仪
您可以将任何对象隐藏在复制真实墙几何图形的虚拟隐形墙后面。配备 LiDAR(光检测和测距)扫描仪的 iPhone 12 Pro 和 iPad Pro 第 4 代可帮助我们重建周围环境的 3D 拓扑图。LiDAR 扫描仪极大地提高了 Z 通道的质量,允许从 AR 场景中遮挡或移除人类。
LiDAR 还改进了对象遮挡、运动跟踪和光线投射等功能。使用 LiDAR 扫描仪,即使在没有照明的环境中或在没有任何特征的白墙房间中,您也可以重建场景。由于sceneReconstruction
实例属性,ARKit 4.0 中可以对周围环境进行 3D 重建。拥有重建的墙壁网格,现在可以非常轻松地将任何物体隐藏在真实墙壁后面。
要sceneReconstruction
在 ARKit 4.0 中激活实例属性,请使用以下代码:
@IBOutlet var arView: ARView!
arView.automaticallyConfigureSession = false
guard ARWorldTrackingConfiguration.supportsSceneReconstruction(.mesh)
else { return }
let config = ARWorldTrackingConfiguration()
config.sceneReconstruction = .mesh
arView.debugOptions.insert([.showSceneUnderstanding])
arView.environment.sceneUnderstanding.options.insert([.occlusion])
arView.session.run(config)
此外,如果您使用的是 SceneKit,请尝试以下方法:
@IBOutlet var sceneView: ARSCNView!
func renderer(_ renderer: SCNSceneRenderer,
nodeFor anchor: ARAnchor) -> SCNNode? {
guard let meshAnchor = anchor as? ARMeshAnchor
else { return nil }
let geometry = SCNGeometry(arGeometry: meshAnchor.geometry)
geometry.firstMaterial?.diffuse.contents =
colorizer.assignColor(to: meshAnchor.identifier)
let node = SCNNode()
node.name = "Node_\(meshAnchor.identifier)"
node.geometry = geometry
return node
}
func renderer(_ renderer: SCNSceneRenderer,
didUpdate node: SCNNode,
for anchor: ARAnchor) {
guard let meshAnchor = anchor as? ARMeshAnchor
else { return }
let newGeometry = SCNGeometry(arGeometry: meshAnchor.geometry)
newGeometry.firstMaterial?.diffuse.contents =
colorizer.assignColor(to: meshAnchor.identifier)
node.geometry = newGeometry
}
这是SCNGeometry
和SCNGeometrySource
扩展:
extension SCNGeometry {
convenience init(arGeometry: ARMeshGeometry) {
let verticesSource = SCNGeometrySource(arGeometry.vertices,
semantic: .vertex)
let normalsSource = SCNGeometrySource(arGeometry.normals,
semantic: .normal)
let faces = SCNGeometryElement(arGeometry.faces)
self.init(sources: [verticesSource, normalsSource], elements: [faces])
}
}
extension SCNGeometrySource {
convenience init(_ source: ARGeometrySource, semantic: Semantic) {
self.init(buffer: source.buffer, vertexFormat: source.format,
semantic: semantic,
vertexCount: source.count,
dataOffset: source.offset,
dataStride: source.stride)
}
}
...和SCNGeometryElement
和SCNGeometryPrimitiveType
扩展:
extension SCNGeometryElement {
convenience init(_ source: ARGeometryElement) {
let pointer = source.buffer.contents()
let byteCount = source.count *
source.indexCountPerPrimitive *
source.bytesPerIndex
let data = Data(bytesNoCopy: pointer,
count: byteCount,
deallocator: .none)
self.init(data: data, primitiveType: .of(source.primitiveType),
primitiveCount: source.count,
bytesPerIndex: source.bytesPerIndex)
}
}
extension SCNGeometryPrimitiveType {
static func of(type: ARGeometryPrimitiveType) -> SCNGeometryPrimitiveType {
switch type {
case .line: return .line
case .triangle: return .triangles
}
}
}
很好的解决方案:
为我工作。
但就我而言,我想通过代码设置墙壁。因此,如果您不想通过用户设置墙壁 -> 使用平面检测来检测墙壁并通过代码设置墙壁。
或者在 4 米范围内,iphone 深度传感器工作,您可以使用ARHitTest检测障碍物。