Apple在WWDC17上宣布了一个名为ARKit的新iOS框架。它是一个“允许您轻松为iPhone和iPad创建无与伦比的增强现实体验”的框架。该框架随iOS 11一起发布(目前处于测试阶段),并且仅由Apple的A9或A10芯片驱动的iOS设备支持。这意味着它不适用于iPhone 5S或iPad Mini等旧设备。此外,您无法在模拟器中使用它,因此您必须使用最新的测试版更新您的iPhone / iPad(iOS 11 SDK仅适用于Xcode 9)。
AR - 使用相机创建虚拟对象放置在物理世界中的错觉。
我们知道增强现实并不是新的,但由于Apple的新框架,AR现在正受到很多关注。Pokemon Go是第一个也许是最着名的应用程序之一,它向我们展示了应用程序中AR的强大功能。实现与Pokemon Go具有相同交互性的应用程序并不容易,这就是为什么我认为ARKit会有所作为。
通过这个新框架,通过为iOS提供本机AR支持,开发人员可以更方便地访问AR。它使用相机传感器进行照明估算,它可以分析相机视图所呈现的内容,并找到像桌子和地板一样的水平平面,它可以在锚点上放置和跟踪物体。您甚至可以使用Metal,SceneKit和Unity和虚幻引擎等第三方工具渲染3D对象。ARKit以卓越的性能完成所有这一切,并且有很好的文档记录。
需要一些想法才能使用ARKit?您可以查看madewitharkit,并通过在您的应用上加入此框架来了解可能实现的目标。
使用ARKit测量对象
我真正喜欢的项目之一是“AR Measure App Demo”:
他们创造了一个精确的虚拟标尺,与真实标尺相比,我惊呆了。我心想:“我需要试试这个!”,所以我决定使用ARKit创建自己的测量应用程序。
我开始观看介绍ARKit:来自WWDC17的iOS增强现实视频。然后我阅读文档并使用演示应用程序(在增强现实中放置对象)。在那之后,我了解了我可以使用什么以及如何工作。从演示中,我了解到场景单元映射到ARKit中的米,所以这是一个很好的提示。
两个节点之间的距离
我想要一个基本的应用程序,只需点击屏幕选择点并计算最后一个点击与前一个点的距离。所以,我使用Swift和SceneKit创建了一个新项目:
创建项目步骤1创建项目步骤1
创建项目步骤2创建项目步骤2
“增强现实应用程序”模板为我们提供了一个基本代码。有一个ViewController
实现AR场景视图delegate(ARSCNViewDelegate
),它已经有一个ARSCNView
很好的IB出口,因为这是用于使用相机显示带有3D SceneKit内容的AR的视图。
免责声明:我使用SceneKit玩过一次,所以我对它有一些基本的了解。如果您没有这些知识或任何3D渲染,如Metal,OpenGL或Unity,那么我建议您在使用ARKit之前查看其中一个,因为它将帮助您理解我将呈现的代码(例如,矢量和矩阵等3D概念以及可以对它们执行的一般操作。
我删除了加载ship.scn
资源的当前场景,viewDidLoad
因为我想从干净的环境开始(在摄像机视图中没有任何内容)。
然后我UITapGestureRecognizer
在主视图中添加了一个以识别用于添加节点的轻击手势。A SCNNode
是“场景图的结构元素,表示3D坐标空间中的位置和变换”,其中可以附加几何图形,灯光,相机或其他可显示内容。我决定使用球体作为几何体。我希望节点位于摄像机前方10厘米处,因此我需要当前帧才能访问摄像机在世界坐标空间中的位置和方向。
红色是“x”轴,绿色是“y”轴,蓝色是“z”轴。
为了实现10厘米的平移,我需要在第四列上应用转换z
。正值定义为更接近相机,负值更远。因此,如果使用0
,对象位置将位于当前相机框架的正前方。
@objc func handleTapGesture(sender: UITapGestureRecognizer) {
if sender.state != .ended {
return
}
guard let currentFrame = sceneView.session.currentFrame else {
return
}
// Create a transform with a translation of 0.1 meters (10 cm) in front of the camera
var translation = matrix_identity_float4x4
translation.columns.3.z = -0.1
// Add a node to the session
let sphere = SCNSphere(radius: 0.005)
sphere.firstMaterial?.diffuse.contents = UIColor.red
sphere.firstMaterial?.lightingModel = .constant
sphere.firstMaterial?.isDoubleSided = true
let sphereNode = SCNNode(geometry: sphere)
sphereNode.simdTransform = matrix_multiply(currentFrame.camera.transform, translation)
sceneView.scene.rootNode.addChildNode(sphereNode)
}
这translation
是一个包含4行和4列的矩阵。这就是3D点的表示方式,可以应用平移,缩放,旋转,反射,倾斜等变换(通过搜索可以更好地理解OpenGL Matrices
)。
最后一步是计算两个节点之间的距离。使用三维欧氏距离公式可以实现两点的距离:
image.png3D中的欧几里德距离公式
我用结束节点位置(两个3D矢量)减去起始节点位置,得到一个新的矢量,然后我应用了公式|a| = sqrt((ax * ax) + (ay * ay) + (az * az))
。这将给出结果向量的长度,它与说明相同:距节点A和节点B的距离。
func distance(startNode: SCNNode, endNode: SCNNode) -> Float {
let vector = SCNVector3Make(startNode.position.x - endNode.position.x, startNode.position.y - endNode.position.y, startNode.position.z - endNode.position.z)
// Scene units map to meters in ARKit.
return sqrtf(vector.x * vector.x + vector.y * vector.y + vector.z * vector.z)
}
self.distanceLabel.text = String(format: "%.2f", distance(startNode: nodeA, endNode: nodeB)) + "m"
您可以在此处查看此实施。
增强测量
在第一次实现之后,我注意到测量不准确,因为您不能保证节点A和节点B在同一表面中。在那种情况下,我需要平面检测功能。垂直平面检测不是一个特征(但是),但可以用一行代码激活水平平面检测configuration.planeDetection = .horizontal
然后ARKit将自动添加,更改或删除当前会话中的平面锚点。您可以观察通过实施这些变化session(_:didAdd:)
,session(_:didUpdate:)
并且session(_:didRemove:)
方法从ARSessionDelegate
委托(请确保您验证是否锚参数是ARPlaneAnchor
)。用户应该知道水平面何时可用,以便开始添加测量点。在苹果公司的ARKit演示实现了一个方形指示器,我认为它可以使用该sceneView.debugOptions
属性,但事实并非如此。
平面检测在行动中
所以,我FocusSquare
从Apple的演示中借用了这个课程。
最后,最后一个问题:如何将节点放在最近的平面上?我已经知道如何将节点放置在摄像机所在的位置,但我如何获得距离最近的平面的距离。答案是:hitTest(_:types:)
。此方法在摄像机图像中搜索视图坐标中指定点的有效曲面,并返回一个列表,其中命中测试结果的排序距离最近(距离摄像机的距离)。
let planeHitTestResults = sceneView.hitTest(view.center, types: .existingPlaneUsingExtent)
if let result = planeHitTestResults.first {
let hitPosition = SCNVector3.positionFromTransform(result.worldTransform)
let sphere = SCNSphere(radius: 0.005)
sphere.firstMaterial?.diffuse.contents = UIColor.red
sphere.firstMaterial?.lightingModel = .constant
sphere.firstMaterial?.isDoubleSided = true
let node = SCNNode(geometry: sphere)
node.position = hitPosition //<--
sceneView.scene.rootNode.addChildNode(node)
}
我假设主视图的中心作为目标,并且作为默认平面,我使用了列表的第一项(最近的平面)。
最后,我实现了session(_:cameraDidChangeTrackingState:)
可以观察设备位置跟踪状态的方法。
func session(_ session: ARSession, cameraDidChangeTrackingState camera: ARCamera) {
switch camera.trackingState {
case .notAvailable:
trackingStateLabel.text = "Tracking not available"
trackingStateLabel.textColor = .red
case .normal:
trackingStateLabel.text = "Tracking normal"
trackingStateLabel.textColor = .green
case .limited(let reason):
switch reason {
case .excessiveMotion:
trackingStateLabel.text = "Tracking limited: excessive motion"
case .insufficientFeatures:
trackingStateLabel.text = "Tracking limited: insufficient features"
case .none:
trackingStateLabel.text = "Tracking limited"
case .initializing:
trackingStateLabel.text = "Tracking limited: initializing"
}
trackingStateLabel.textColor = .yellow
}
}
您可以检查整个项目并在您的设备上进行测试。
我对AR的力量印象深刻。有很多用例可以探索。如果您有任何我们可以帮助您使用AR的项目,请不要犹豫,并与我们联系。
网友评论