WWDC18:ARKit2与ARKit1.5 — 2D跟踪性能比较
去年,最有趣的技术趋势之一就是增强现实。 包括苹果在内的所有大公司都做了与之相关的事情。 他们花了大量的资源和时间来解决这个问题,正如蒂姆·库克(Tim Cook)所说的:
“……增强现实可能是下一件大事……”
如果说2017年是引入一个完整的可处理AR框架的里程碑年,那么2018年无疑是ARKit2引入后该技术成熟的一年。
为了可用性,可维护性和简单性,许多事情已经发生了变化:所有可能有助于将技术大规模传播的事物。 本文的其余部分是关于我对新的ARKit框架的个人经验,重点介绍了iOS11和iOS12之间的改进和区别。 就像任何优秀的开发人员文章一样,我们需要进行案例研究以更好地解释该主题。 所以,就在这里。
我们将创建一个简单的应用程序,该应用程序使用增强现实技术在指出名片时向您显示特别优惠和其他信息。
(可选)应用程序将从remte JSON加载所需的信息,然后在需要时向用户显示相应的视觉增强功能。
首先是打开Xcode并创建一个填充所有相关内容的新AR项目:
默认情况下,Xcode创建一个供使用的起始场景,而该场景对于本文中的内容完全没有用。 删除它并打开为我们自动创建的ViewController.swift文件。
由于我们不再关心自动生成的场景,因此将修改viewDidLoad方法以反映此行为。 我们所需要做的就是用正确的委托实例以及一些用于调试目的的有趣的东西来指示sceneView出口。
覆盖func viewDidLoad(){
super.viewDidLoad()
sceneView.delegate =自我
sceneView.showsStatistics = true
sceneView.autoenablesDefaultLighting = true
sceneView.automaticallyUpdatesLighting = true
}
ARSCNView类是任何AR应用程序的入口点。 它将虚拟内容与通过摄像头设备看到的真实单词混合在一起。 它还负责许多其他非常有趣的东西,例如将触摸映射到现实世界坐标。 ARSCNView依赖于委托方法。 通过实现其ARSCNViewDelegate ,我们可以将虚拟内容添加到ARKit自动检测到的任何真实对象中。
您可能会猜到ARKit是负责增强现实的框架。 为了正常工作,应该首先正确初始化它。 让我们添加一个方法来做到这一点:
右键单击左侧,创建一个新的AR资源组:
将其命名为Photos并添加一些卡片。 这里最重要的是为每张卡添加相对估计的尺寸。 这很重要,因为3d引擎需要此信息来正确估计3d世界中的对象。
您可能会注意到其中一张卡具有警告信号。 让我们点击它:
Xcode警告我们,卡上的直方图和对比度对于图像识别而言并非最佳。 让我们暂时忽略它。
我们的玩具应用程序应该能够使用锚点来跟踪2D图像并将其映射到现实世界中的对象。 ARAnchor是负责现实位置和方向的类,用于在AR场景中映射检测到的对象。 除其他有趣的属性外,它还包含一个matrix_float4x4,用于定义锚点在世界坐标系中的旋转,平移和缩放。
打开var转换:matrix_float4x4 {get}
与ARWorldTrackingConfiguration或ARImageTrackingConfiguration一起使用时,只要在场景中找到与参考图像的匹配项 ,ARKit就会自动为我们创建相对锚点。 我们要做的就是实现相对委托,以便将SCNNode形式的相应对象添加到新锚。
SCNNode是所有基于AR的应用程序的基本乐高积木。 它将3d坐标与场景对象(例如灯光,照相机和其他可显示内容)包装在一起。 至于UIViews , SCNNodes遵循树形层次结构,该层次结构允许我们通过使用简单的原语(例如
打开函数addChildNode(_ child:SCNNode)打开函数removeFromParentNode()
响应我们在图像数据集和真实世界之间检测到的新的对应关系,调用renderer:nodeFor:方法。 由于也响应添加到场景中的任何新ARAnchor (检测到的平面,3d对象等)而调用了此方法,因此我们应针对ARImageAnchor进行测试,以确保所有工作均正常进行:
如果让imageAnchor =锚定为? ARImageAnchor {
…
}
接下来,我们将创建一个新的SCNNode ,其中将包含所有相关的内容,并切换到检测到的图像名称; 这是主要业务逻辑,目的是区分我们数据集中的图像。
切换imageAnchor.referenceImage.name {
案例“ card1”:
…
案例“ card2”:
…
默认:
…
}
然后,如果我们成功创建了一个有效节点,则将其添加到层次结构中并返回主节点:
如果让labelNode = labelNode {
node.addChildNode(labelNode)
…返回节点
}
为了给专业人士一些感觉,我们想在整个工作流程中添加一些淡入/淡出动画。 由于在主线程上调用了reder:nodeFor:,因此我们应该同步runAction ,以确保不会出现糟糕的动画。
DispatchQueue.main.async {
node.runAction(SCNAction.fadeIn(持续时间:3.0))
}
对于更好奇
addLabel是用于生成有效的2D spritekit对象的支持方法。 由于我们正在处理2D跟踪图像,因此我们想在场景上添加2d虚拟内容,以使用户感觉在真实对象上方添加了附加信息。
我们首先用检测到的图像的物理尺寸创建一个SCNPlane 。 SCNPlane是一个二维二维平面几何对象,负责保存我们的二维文本。 但是,由于我们处于3D世界中,因此需要将其包装在SCNNode上。
让planeNode = SCNNode(geometry:plane)
planeNode.eulerAngles.x = -.pi / 2
我们还应该旋转结果平面节点,因为默认情况下它们是垂直于根生成的。 此时,仍不会创建2d对象或文本。 为此,我们需要创建一个SprikeKit Scene 。
在SpriteKit处理的2d世界中, SKScene是SCNScene的对等物 。 它代表2d场景内容,因此无任何显示,因为3d paritetic只是一个包装器。 为了生成2d文本,我们将使用SKLabelNode 。 它的工作原理与UILabel UIKit非常相似:我们以非常简单的方式配置文本,字体和其他参数。
让lbl = SKLabelNode(文本:aSubstring)
lbl.fontSize = 20
lbl.numberOfLines = 1
lbl.fontColor = UIColor.white.withAlphaComponent(0.85)
lbl.fontName =“ Helvetica-Bold”
遗憾的是,没有使用回车符(\ n)来支持多行换行,因此我们需要自己重新创建换行。 为此,我们使用简单的字符串标记化,并以原始字符串作为参数。
let子字符串:[String] = text.components(separatedBy:“ \ n”)
对于子字符串中的子字符串{
…
}
这里最有趣的部分是我们如何在3d世界中映射2d spritekit。 为此,我们使用SCNMaterial 。 SCNMaterial是一个类,负责定义在3D世界中渲染时2D几何图形的外观。 与任何3D引擎一样,我们的SCNScene具有在渲染时对其进行响应的灯光和材质。 我们只需向SCNMaterial指定要使用先前创建的SKScene的内容作为材料。
让材质= SCNMaterial()
material.isDoubleSided = false
material.diffuse.contents = skScene
isDoubleSided属性设置为false,因为我们不在乎文本的背面。 扩散属性是SCNMaterialProperty类。 它只是我们材料质地的容器。 对我们来说幸运的是,它几乎可以包含任何东西,包括整个SKScene 。 最后,我们指示先前生成的飞机使用刚刚制作的定制材料作为来源。
plane.materials = [材料]
如果您遵循了我们现在所做的所有工作并在iOS12上运行了迄今为止的工作,那么所有事情都将像魅力一样运转,令人印象深刻且准确的2d跟踪将使您感到高兴。 但是,如果您在iOS11上运行相同的代码,您将对跟踪性能不佳感到失望。
此外,还有一个非常糟糕的错误,使检测到的2d图像永远留在3d世界中。 如果您还记得的话,我在本文开头说过
ARImageTrackingConfiguration最有趣的新功能之一与屏幕上消失的对象的新处理功能有关。
由于iOS11上没有此类功能,因此我们需要自行创建解决方法。 我们将使用私有结构来保存所有必需的信息:
struct SANodeAnchor {
var date:Date
var node:SCNNode
var anchor:ARAnchor
}
在我们的ViewController上将其声明为私有数组。 我们还需要定期轮询场景,以清除垃圾中的物体
私人var lastUpdate = [String:SANodeAnchor]()
私人varcheduledTimer:计时器?
我们的viewDidAppear方法将负责定义上述计时器
当场景中正确解析了新对象或已检测到的对象时,我们只需要刷新lastUpdate缓存数组即可。 对于此任务,我们将使用ARSCNViewDelegate的另一个委托方法:
这样,即使在iOS11上,我们也可以成功停止从屏幕上跟踪不需要的2d对象。
完整的项目可在github上找到。 在撰写本文时,Xcode10困扰着一个错误,该错误阻止了在部署到iOS11设备时使用数据集图像。
从Xcode10部署到iOS11设备时,ARWorldTrackingConfiguration不起作用
在基于ARWorldTrackingConfiguration的概念代码上工作。 从……部署时,似乎无法正常工作
stackoverflow.com
结果,在iOS11上,从Xcode10部署时,该应用将不会检测到任何2d图像。 要解决此问题,在iOS11设备上部署时,应在Xcode9上运行上述项目,并注释与ARImageTrackingConfiguration相关的未知符号。
加载远程数据集
关于加载动态数据集的最后说明。 上面的代码依赖于我们存储在数据集中的静态数据集。 但是,最好从远程服务器动态加载数据集。 我为这个目的设计了一个非常简单的JSON示例。
[
{
“ imageURL”:“ https://www.sofapps.it/card1.jpg”,
“ name”:“ card1”,
“宽度”:0.096
},
{
“ imageURL”:“ https://www.sofapps.it/card2.jpg”,
“ name”:“ card2”,
“宽度”:0.058
}
]
让我们从应用程序中获取此json:
并动态加载卡:
我们的resetTrackingConfiguration方法还应该进行调整,以考虑到运行时的负载:
此时,该应用程序应可在iOS11和iOS12上运行,并从服务器中动态加载数据集内容。
-编码愉快!