ARKit框架详细解析(四)—— 处理增强现实中的3D交互和UI
版本记录
版本号 | 时间 |
---|---|
V1.0 | 2017.09.27 |
前言
苹果最近新出的一个API就是ARKit,是在2017年6月6日,苹果发布iOS11系统所新增框架,它能够帮助我们以最简单快捷的方式实现AR技术功能。接下来几篇我们就详细的对ARKit框架进行详细的解析。AR相关代码已经上传到Github - 刀客传奇,感兴趣的可以看上面几篇。
1. ARKit框架详细解析(一)—— 基本概览
2. ARKit框架详细解析(二)—— 关于增强现实和ARKit
3. ARKit框架详细解析(三)—— 开启你的第一个AR体验之旅
Overview - 概览
同样是看一下上传到Github
仓库中的代码,遵循AR体验中的视觉反馈,手势交互和逼真渲染的最佳做法。
增强现实(AR)为用户提供了与您的应用程序中的真实和虚拟3D内容进行交互的新方式。 然而,人机接口设计的许多基本原理仍然有效。 令人信服的AR幻想也需要仔细注意3D资源设计和渲染。 iOS Human Interface Guidelines 包括有关AR接口原理的建议。 该项目显示了应用这些准则的方法,并轻松创建沉浸式,直观的AR体验。
此示例应用程序提供了简单的AR体验,允许用户将一个或多个逼真的虚拟对象放置在其现实环境中,然后使用直观的手势来排列这些对象。 该应用程序提供用户界面提示,以帮助用户了解AR体验的状态及其交互选项。
以下部分对应于iOS Human Interface Guidelines > Augmented Reality部分,并提供有关此示例应用程序如何实施这些准则的详细信息。 有关每个部分的更详细的推理,请参阅 iOS Human Interface Guidelines中相应的内容。
Placing Virtual Objects - 放置虚拟对象
帮助人们了解何时找到一个表面并放置一个对象。 FocusSquare
类在AR视图中绘制一个方形轮廓,为用户提供关于ARKit世界跟踪状态的提示。
该方块改变大小和方向以反映估计的场景深度,并用突出的动画切换打开和关闭状态,以指示ARKit是否检测到适合放置对象的平面。 在用户放置虚拟对象后,焦点方格消失,保持隐藏,直到用户将相机指向另一个表面。
当用户放置对象时,适当地响应。 当用户选择要放置的虚拟对象时,示例应用程序的setPosition(_:relativeTo:smoothMovement)
方法使用FocusSquare
对象的简单启发式方法将对象放置在屏幕中间大致逼真的位置,即使ARKit
尚未 在该地点侦测到一个平面。
下面看一下代码示例
guard let cameraTransform = session.currentFrame?.camera.transform,
let focusSquarePosition = focusSquare.lastPosition else {
statusViewController.showMessage("CANNOT PLACE OBJECT\nTry moving left or right.")
return
}
virtualObjectInteraction.selectedObject = virtualObject
virtualObject.setPosition(focusSquarePosition, relativeTo: cameraTransform, smoothMovement: false)
这个位置可能不是用户想要放置虚拟对象的真实表面的准确估计,但是它足够接近以快速获得对象。
随着时间的推移,ARKit会检测到平面并调整其位置的估计,调用渲染器: renderer:didAddNode:forAnchor: 和 renderer:didUpdateNode:forAnchor:代理方法来报告结果。 在这些方法中,示例应用程序调用其adjustOntoPlaneAnchor(_:using :)
方法来确定先前放置的虚拟对象是否接近检测到的平面。 如果是这样,该方法使用微妙的动画将虚拟对象移动到平面上,从而使对象看起来处于用户选择的位置,同时受益于ARKit对该位置的真实表面的精确估计:
// Move onto the plane if it is near it (within 5 centimeters).
let verticalAllowance: Float = 0.05
let epsilon: Float = 0.001 // Do not update if the difference is less than 1 mm.
let distanceToPlane = abs(planePosition.y)
if distanceToPlane > epsilon && distanceToPlane < verticalAllowance {
SCNTransaction.begin()
SCNTransaction.animationDuration = CFTimeInterval(distanceToPlane * 500) // Move 2 mm per second.
SCNTransaction.animationTimingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
position.y = anchor.transform.columns.3.y
SCNTransaction.commit()
}
User Interaction with Virtual Objects - 用户与虚拟对象的交互
- 允许人们使用标准熟悉的手势直接与虚拟对象进行交互。
示例应用程序使用单指点击,单指和双指平移以及双指旋转手势识别器来让用户定位和定向虚拟对象。 示例代码的VirtualObjectInteraction
类管理这些手势。
- 一般来说,保持互动简单。
当拖动虚拟对象(请参阅translate(_:basedOn:infinitePlane :)
方法)时,示例应用程序将对象的移动限制为放置在其上的二维平面。 类似地,由于虚拟对象依赖于水平平面,旋转手势(请参阅didRotate(_ :)
方法)仅绕其垂直轴旋转对象,以使对象保留在平面上。
- 在交互式虚拟物体的合理接近范围内回应手势。
示例代码的objectInteracting(with : in :)
方法使用手势识别器提供的触摸位置来执行命中测试。 通过对虚拟对象的边界框进行打击测试,该方法使得用户触摸更可能影响对象,即使触摸位置不在对象具有可见内容的点上。 通过对多点触控手势执行多次命中测试,该方法使得用户触摸更可能影响预期对象:
for index in 0..<gesture.numberOfTouches {
let touchLocation = gesture.location(ofTouch: index, in: view)
// Look for an object directly under the `touchLocation`.
if let object = sceneView.virtualObject(at: touchLocation) {
return object
}
}
// As a last resort look for an object under the center of the touches.
return sceneView.virtualObject(at: gesture.center(in: view))
- 考虑用户启动的对象缩放是否必要。
这个AR经验会放置可能自然地出现在用户环境中的现实虚拟对象,因此保留对象的内在大小有助于实现现实。 因此,示例应用程序不会添加手势或其他UI来启用对象缩放。 另外,通过不包括缩放手势,示例应用程序防止用户对于手势是调整对象大小还是改变对象距离相机的距离而感到困惑。 (如果您选择在应用程序中启用对象缩放,请使用捏捏手势识别器。)
- 警惕潜在的冲突手势。
示例代码的ThresholdPanGesture
类是一个UIPanGestureRecognizer
子类,它提供一种延迟手势识别器效果的方法,直到正在进行的手势通过指定的移动阈值。 示例代码的touchesMoved(with :)方法使用此类让用户在拖动对象之间平滑过渡并在单个双指手势中旋转对象:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesMoved(touches, with: event)
let translationMagnitude = translation(in: view).length
// Adjust the threshold based on the number of touches being used.
let threshold = ThresholdPanGesture.threshold(forTouchCount: touches.count)
if !isThresholdExceeded && translationMagnitude > threshold {
isThresholdExceeded = true
setTranslation(.zero, in: view)
}
}
- 确保虚拟对象的移动平滑。
示例代码的setPosition(_:relativeTo:smoothMovement)
方法在导致拖动对象的触摸手势位置和该对象的最近位置的历史记录之间插值。 通过根据距离摄像机的距离平均最近的位置,该方法可以产生平滑的拖动运动,而不会使拖动的对象滞后于用户的手势:
if smoothMovement {
let hitTestResultDistance = simd_length(positionOffsetFromCamera)
// Add the latest position and keep up to 10 recent distances to smooth with.
recentVirtualObjectDistances.append(hitTestResultDistance)
recentVirtualObjectDistances = Array(recentVirtualObjectDistances.suffix(10))
let averageDistance = recentVirtualObjectDistances.average!
let averagedDistancePosition = simd_normalize(positionOffsetFromCamera) * averageDistance
simdPosition = cameraWorldPosition + averagedDistancePosition
} else {
simdPosition = cameraWorldPosition + positionOffsetFromCamera
}
- 探索更有吸引力的互动方式。
在AR体验中,平移手势 - 即将手指移动到设备的屏幕上 - 并不是将虚拟内容拖到新位置的唯一自然方法。 用户还可以直观地尝试在移动设备的同时将手指保持在屏幕上,有效地将触摸点拖过AR场景。
示例应用程序通过在拖动手势正在进行时持续调用其updateObjectToCurrentTrackingPosition()
方法来支持这种手势,即使手势的触摸位置没有改变。 如果设备在拖动期间移动,则该方法计算与触摸位置相对应的新世界位置,并相应地移动虚拟对象。
Entering Augmented Reality - 进入增强现实
指示初始化发生时涉及用户。 示例应用程序显示有关AR会话状态的文本提示以及使用浮动文本视图与AR体验进行交互的说明。 示例代码的StatusViewController
类管理此视图,显示允许用户读取它们之后淡出的临时指令,或重要的状态消息,直到用户更正问题为止。
处理问题 - Handling Problems
- 如果不符合他们的期望,允许人们重置体验。
示例应用程序具有始终在UI右上角可见的重置按钮,允许用户重新启动AR体验,无论其当前状态如何。 请参阅示例代码中的restartExperience()
方法。
仅在有能力的设备上提供AR功能。 示例应用程序需要ARKit的核心功能,因此它在Info.plist
文件的一部分中定义了arkit密钥。 部署内置项目时,此键可防止在不支持ARKit的设备上安装应用程序。
如果您的应用程序使用AR作为辅助功能,请使用该方法来确定是否隐藏需要ARKit的功能。
后记
未完,待续~~~