Ios ARKit将对象隐藏在墙后
如何使用ARKit跟踪的水平和垂直平面隐藏墙后/真实对象后的对象?当前,当您离开房间和/或在应该在后面的对象前面时,可以通过墙看到添加的三维对象。那么,有没有可能使用ARKit提供的数据来提供更自然的AR体验,而不让物体穿过墙壁?这里有两个问题 (而你甚至没有!) 如何为ARKit/SceneKit创建遮挡几何体? 如果将SceneKit材质设置为空值(Ios ARKit将对象隐藏在墙后,ios,swift,scenekit,augmented-reality,arkit,Ios,Swift,Scenekit,Augmented Reality,Arkit,如何使用ARKit跟踪的水平和垂直平面隐藏墙后/真实对象后的对象?当前,当您离开房间和/或在应该在后面的对象前面时,可以通过墙看到添加的三维对象。那么,有没有可能使用ARKit提供的数据来提供更自然的AR体验,而不让物体穿过墙壁?这里有两个问题 (而你甚至没有!) 如何为ARKit/SceneKit创建遮挡几何体? 如果将SceneKit材质设置为空值([]在Swift中),则使用该材质的任何对象都不会显示在视图中,但在渲染期间它们仍会写入z缓冲区,这会影响其他对象的渲染。实际上,您将得到一个形
[]
在Swift中),则使用该材质的任何对象都不会显示在视图中,但在渲染期间它们仍会写入z缓冲区,这会影响其他对象的渲染。实际上,您将得到一个形状类似于对象的“孔”,通过该孔可以显示背景(在ARSCNView
的情况下,相机馈送),但它仍然可以遮挡其他SceneKit对象
您还需要确保被遮挡的节点在其应该遮挡的任何其他节点之前进行渲染。可以使用节点层次结构(我不记得父节点是在其子节点之前渲染,还是以其他方式渲染,但测试起来很容易)。作为层次结构中对等节点的节点没有确定的顺序,但您可以使用属性强制执行顺序,而不考虑层次结构。该属性默认为零,因此将其设置为-1将在渲染所有内容之前进行渲染。(或者,对于更精细的控制,请将多个节点的渲染顺序设置为一系列值。)
如何检测墙/等以便知道在何处放置遮挡几何体?
在iOS 11.3及更高版本(又名“ARKit 1.5”)中,可以启用平面检测。(请注意,当您从中获取平面定位点时,它们会自动旋转。因此,如果将模型附加到定位点,则其局部“向上”方向与平面垂直。)也是iOS 11.3中的新功能,您可以获得每个检测到的平面的更详细形状估计(请参见),而不管其方向如何
然而,即使你有水平线和垂直线,平面的外部界限也只是随着时间变化的估计值。也就是说,ARKit可以快速检测出墙的一部分在哪里,但如果用户不花时间挥动设备绘制空间图,它就不知道墙的边缘在哪里。即使如此,贴图的边也可能与真实墙的边不完全对齐
所以。。。如果使用检测到的垂直平面来遮挡虚拟几何体,则可能会找到本应隐藏的虚拟对象的显示位置,或者在墙的边缘不完全隐藏,或者在ARKit未映射整个真实墙的位置可见。(后一个问题您可以通过假设比ARKit更大的范围来解决。)伟大的解决方案:
为我工作
但在我的情况下,我想按代码设置墙。因此,如果不想按用户设置墙->使用平面检测检测墙并按代码设置墙
或者在4米范围内,iphone depht传感器工作,您可以检测障碍物。要创建遮挡材质(也称为黑洞材质或遮挡材质),您必须使用以下实例属性:
.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
…或者这样:
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
ARKit 4和激光雷达扫描仪
可以将任何对象隐藏在复制真实墙几何图形的虚拟不可见墙后面。iPhone12Pro和iPadPro第四代配备了激光雷达(光探测和测距)扫描仪,帮助我们重建周围环境的三维拓扑图。激光雷达扫描仪极大地提高了Z通道的质量,允许从雷达场景中遮挡或移除人类
激光雷达还改进了诸如物体遮挡、运动跟踪和光线投射等功能。使用激光雷达扫描仪,您可以重建场景,即使在没有照明的环境中,或者在没有任何特征的白墙房间中。得益于scenerconstruction
instance属性,Arkit4.0环境的三维重建成为可能。有了一个重建的墙壁网格,现在可以非常容易地将任何物体隐藏在真实的墙壁后面
要激活ARKit 4.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)
}
}
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
}
}
}
…和SCNGeometryElement
和SCNGeometryPrimitiveType
扩展名:
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)
}
}
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
}
}
}
因此,文档中的示例代码有一个选项,用于
configuration.planeDetection=.horizontal
,它什么也不做?该代码打开了平面检测。如果没有它,ARKit根本不会报告平面。是的,但我的意思是,如果你能将平面检测设置为水平,你应该能够将其设置为水平vertical@Steve(在Xcode中)可以跳转到定义(⌃⌘单击.horizontal
,您将在那里找不到其他选项。如果苹果用“垂直”扩展了选项集,不过将来可能还会有其他类型的平面。嗨,瑞克,部分遮挡会比这项技术复杂得多吗?你能根据你看到的和你期望的添加图片吗?(我对ARKit一无所知,但仍然无法理解你的问题)只有当你有ARView而不是ARSCNView时,这才有效。您知道如何使用ARSCNView完成此操作吗?在这里您可以了解如何:。使用遮挡材质而不是彩色材质。请您描述一下如何使用ARSCNView进行遮挡?谢谢你。@pavelcauselov,我添加了一个触摸SceneKit的代码