探索ARKit:ARSCNPlaneGeometry

如果您听说过,Apple最近发布了ARKit的主要更新,称其为ARKit 1.5。 虽然很多注意力都集中在新的垂直平面检测功能上,而ARKit最初的版本已经明显忽略了这些功能,但关于其他新的平面检测功能的说法却很少。 令人兴奋的是失去了一些非常强大的新功能。 有争议的是,苹果公司的ARKit版本中最酷的功能是很少被提及的功能。

隐藏起来,我们为ARPlaneAnchor添加了一些新属性。 花一点时间看看这些。 在检测曲面的存在和位置/尺寸之前,在ARKit 1.5中,我们现在获取了轮廓和地面的位置。

ARPlaneGeometry — 3D网格,描述了在世界跟踪的AR会话中检测到的平面的形状。

此类以详细的3D网格形式提供检测到的平面的估计总体形状,该3D网格适用于各种渲染技术或导出3D资产。 与ARPlaneAnchor centerextent属性不同,后者仅估计检测到的平面的矩形区域,而平面锚的geometry属性则提供了该平面覆盖的2D区域的更详细的估计。 例如,如果ARKit检测到圆形桌面,则生成的ARPlaneGeometry对象将大致匹配表的一般形状。 随着会话的继续进行,ARKit提供了更新的平面锚,其关联的几何形状完善了平面的估计形状。您可以使用此模型更精确地放置应仅出现在检测到的平面上的3D内容-例如,确保虚拟对象不会掉到桌子的边缘。 您还可以使用此模型创建遮挡几何图形,该遮挡几何图形会将其他虚拟内容隐藏在相机图像中检测到的表面后面。平面几何图形的形状始终为凸形。  (也就是说,平面几何图形的边界多边形是一个最小的凸包,其中包含ARKit识别或估计的所有点都是该平面的一部分。) 

vertices-平面网格中每个点的顶点位置的缓冲区。

 此缓冲区中的每个float3值代表坐标系统中顶点在网格中的位置,坐标系的原点由所属平面锚点的变换矩阵定义。vertexCount属性提供缓冲区中元素的数量。 triangleIndices缓冲区,描述覆盖平面整个表面的网格。 将此网格用于涉及填充形状的目的,例如渲染曲面的实体3D表示。 如果相反,您只需要知道形状的轮廓,请参阅boundaryVertices属性。 

boundaryVertices —沿平面边界的每个点的顶点位置的数组。

 该数组中的每个float3值均代表坐标系统中顶点沿估计平面边界多边形的位置,该坐标系的原点由所属平面锚点的transform矩阵定义。此数组定义平面的边界多边形。 将其用于仅需要该多边形的定义的目的,例如绘制平面的估计形状的轮廓或测试点是否在边界区域内。 如果相反,您需要填充的形状(例如,渲染曲面的实体3D表示),请参见vertices属性。 

textureCoordinates —平面网格中每个点的纹理坐标值的缓冲区。

 此缓冲区中的每个float2值代表vertices缓冲区中相应索引处顶点的UV纹理坐标。 

ARSCNPlaneGeometry —平面的2D形状的SceneKit表示形式,用于AR会话中的平面检测结果。

 此类是SCNGeometry的子类, SCNGeometry包装ARPlaneGeometry类提供的网格数据。 您可以使用ARSCNPlaneGeometryARSCNPlaneGeometry中快速轻松地可视化ARKit提供的平面形状估计。随着您的AR会话继续运行,ARKit会提供对检测到的平面2D形状的精确估计。 使用updateFromPlaneGeometry:方法将这些改进合并到平面的SceneKit表示中。 

这是一台基本的3D扫描仪,尽管其准确度比使用激光传感器的3D扫描仪要低。
无论如何,这是巨大的。 多年来,人们一直在谈论这样一个时刻,即人们可以根据自己的环境快速便捷地创建3D扫描,以及可能带来的可能性。 好了,我们到了(尽管有一些警告)

首先,无论有没有平面几何的最新更新,我们都已经准备就绪。 据报道,苹果公司一直在寻求在其下一代iPhone型号中增加背面3D相机,以增强增强现实,因此,这只是我们今年夏天可能会看到的内容的预览。 这也不是最顺畅的用户体验,要完全检测到对象将非常耗时。 但是,看起来今年夏天有很多期待! 如果我不得不猜测,Apple可能会针对所有类型的表面/形状推出可用的表面检测。 希望2018年ARKit中所有遮挡难题也能结束!

 导入UIKit 
导入SceneKit
导入ARKit
类ViewController:UIViewController {


@IBOutlet var sceneView:ARSCNView!

专用var planeId:Int = 0

让standardConfiguration:ARWorldTrackingConfiguration = {
让配置= ARWorldTrackingConfiguration()
configuration.planeDetection = [.horizo​​ntal,.vertical]
configuration.isLightEstimationEnabled = true
返回配置
}()

覆盖func viewDidLoad(){
super.viewDidLoad()
}

func runSession(){
sceneView.delegate =自我
sceneView.session.run(standardConfiguration)
#if调试
sceneView.showsStatistics = true
sceneView.debugOptions = [
ARSCNDebugOptions.showFeaturePoints,
ARSCNDebugOptions.showWorldOrigin
]
#万一
}

覆盖func viewWillAppear(_动画:布尔){
super.viewWillAppear(动画)
runSession()
}
}
 扩展SCNNode { 

静态函数createPlaneNode(planeAnchor:ARPlaneAnchor,id:Int)-> SCNNode {
让scenePlaneGeometry = ARSCNPlaneGeometry(设备:MTLCreateSystemDefaultDevice()!)
scenePlaneGeometry?.update(来自:planeAnchor.geometry)
让planeNode = SCNNode(geometry:scenePlaneGeometry)
planeNode.name =“ \(id)”
切换planeAnchor.alignment {
大小写
planeNode.geometry?.firstMaterial?.diffuse.contents = UIColor.blue.withAlphaComponent(0.3)
大小写.vertical:
planeNode.geometry?.firstMaterial?.diffuse.contents = UIColor.cyan.withAlphaComponent(0.5)

}
返回planeNode
}

}
  //标记:-ARSCNViewDelegateextension ViewController:ARSCNViewDelegate {功能渲染器(_渲染器:SCNSceneRenderer,didAdd节点:SCNNode,用于锚点:ARAnchor){ 
守卫让planeAnchor =锚定为? ARPlaneAnchor else {return}
让planeNode = SCNNode.createPlaneNode(planeAnchor:planeAnchor,id:planeId)
planeId + = 1
node.addChildNode(planeNode)
}

func renderer(_ renderer:SCNSceneRenderer,didUpdate节点:SCNNode,锚点:ARAnchor){
守卫让planeAnchor =锚定为? ARPlaneAnchor else {return}
node.enumerateChildNodes {child,_ in
child.removeFromParentNode()
}
让planeNode = SCNNode.createPlaneNode(planeAnchor:planeAnchor,id:planeId)
planeId + = 1
node.addChildNode(planeNode)
}

func renderer(_ renderer:SCNSceneRenderer,didRemove节点:SCNNode,对于锚点:ARAnchor){
守卫让_ =锚定为? ARPlaneAnchor else {return}
node.enumerateChildNodes {child,_ in
child.removeFromParentNode()
}
}
}