我如何利用ARKit跟踪的水平和垂直平面,将对象隐藏在墙壁/真实物体后面?当前,当您离开房间和/或在它们应该在后面的物体前面时,可以穿过墙壁看到添加的3D对象。因此,是否可以使用ARKit提供给我的数据,提供更自然的AR体验,而不会让对象穿过墙壁呢?
我如何利用ARKit跟踪的水平和垂直平面,将对象隐藏在墙壁/真实物体后面?当前,当您离开房间和/或在它们应该在后面的物体前面时,可以穿过墙壁看到添加的3D对象。因此,是否可以使用ARKit提供给我的数据,提供更自然的AR体验,而不会让对象穿过墙壁呢?
您有两个问题。
(而且您甚至没有 使用正则表达式!)
如果将 SceneKit 材质的 colorBufferWriteMask
设置为空值(在 Swift 中为 []
),使用该材质的任何对象都不会出现在视图中,但它们仍会在渲染期间写入 z 缓冲区,从而影响其他对象的渲染。实际上,您将获得一个形状类似于您的对象的“孔”,通过这个“孔”,可以看到背景(在 ARSCNView
的情况下是相机反馈),但它仍然可以遮挡其他 SceneKit 对象。
renderingOrder
属性强制指定顺序,无论层次结构如何。该属性默认为零,因此将其设置为-1将在所有内容之前呈现。(或者为了更精细的控制,将几个节点的renderingOrder
设置为一系列值。)
垂直
平面检测。 (请注意,当您从中获取垂直
平面锚点时,它们会自动旋转。因此,如果您将模型附加到锚点上,则它们的本地“向上”方向与平面法线相同。)另外,在iOS 11.3中还可以获得每个检测到的平面的更详细的形状估计(请参见ARSCNPlaneGeometry),无论其方向如何。
然而,即使您拥有水平和垂直方向,平面的外部限制只是随时间变化的估计值。也就是说,ARKit可以快速检测出墙壁的一部分位置,但是如果没有用户花费一些时间来映射空间,它不知道墙壁的边缘在哪里。即使这样,映射出的边缘可能也不会与真实墙壁的边缘完全对齐。
如果您使用检测到的竖直平面来遮挡虚拟几何体,您可能会发现一些本应隐藏的虚拟对象通过显示出来,可能是因为在墙壁边缘没有完全隐藏或者在ARKit没有映射整个真实墙壁的地方可见。(您可以通过假设比ARKit更大的范围来解决后一个问题。).horizontal
的定义(⌃⌘单击),并且您将在那里找不到其他选项。如果Apple扩展了选项集以包括“vertical”-以及可能在未来的其他平面类型,我也不会感到惊讶。 - PDK要创建遮挡材质(也称为黑洞材质或阻塞材质),您需要使用以下实例属性:.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
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
很好的解决方案:
对我有用。
但在我的情况下,我想通过代码设置墙壁。因此,如果您不想让用户设置墙壁->使用平面检测来检测墙壁并通过代码设置墙壁。
或者在4米范围内,iPhone深度传感器可以工作,并且您可以使用ARHitTest检测障碍物。
您可以将任何对象隐藏在虚拟的不可见墙后面,这个墙会复制真实墙壁的几何形状。配备有LiDAR扫描仪的iPhone和iPad Pro帮助我们重建周围环境的三维拓扑地图。 LiDAR扫描仪极大地提高了Z通道的质量,使得可以遮挡或从AR场景中移除人物。
此外,LiDAR还改进了诸如对象遮挡、运动跟踪和射线投射等功能。使用LiDAR扫描仪,即使在没有光源的环境或完全没有特征的白墙房间中,也可以重建场景。在ARKit 6.0中,通过sceneReconstruction
实例属性,周围环境的3D重建成为可能。有了重建的墙壁网格,现在可以轻松地将任何对象隐藏在真实的墙壁后面。
要在ARKit 6.0中激活sceneReconstruction
实例属性,请使用以下代码:
@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
}
}
}