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的功能。

后记

未完,待续~~~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容