AR酱 ARKit从入门到精通(3)--平面检测

5
回复
1517
查看
打印 上一主题 下一主题
[ 复制链接 ]
排名
2297
昨日变化

37

主题

265

帖子

1535

积分

Rank: 9Rank: 9Rank: 9

UID
156756
好友
11
蛮牛币
1007
威望
0
注册时间
2016-7-13
在线时间
539 小时
最后登录
2020-3-31

专栏作家

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

x
本帖最后由 geekli 于 2019-10-21 21:02 编辑

本部分主要通过ARKit实现平面检测并在其上面放置一个虚拟物体。

效果预览:



在正式开始前,我们需要了解“Horizontal Plane”这一概念。当我们在ARKit中讨论水平面时,我们究竟在讨论什么呢?当我们在ARKit中检测到一个水平面时,我们在技术上检测到的其实是一个ARPlaneAnchor。那么什么是ARPlaneAnchor呢?ARPlaneAnchor大体上可以理解为一个包含检测到的水平面信息的对象(水平与位置信息)。

本部分主要介绍Horizontal Plane的实现方式,所以我们直接在这个工程(链接: https://pan.baidu.com/s/1eJeIbkzk1OUy_YLk6X3yog 提取码: vv6y )上面继续开发,对于如何在Xcode使用ARKit实现AR效果不懂的可以查看之前的文章:

下载完后在Xcode打开,并运行项目,如下图所示:



Step 1: 水平面检测

在ViewController的setUpSceneView()方法中添加以下内容:

[AppleScript] 纯文本查看 复制代码
configuration.planeDetection = .horizontal


通过将ARWorldTrackingConfiguration的planeDetection属性设置为.horizontal,这告诉ARKit去查找任何水平面。一旦ARKit检测到一个水平面,该水平面将被添加到sceneView的session中。

为了检测水平面,必须采用ARSCNViewDelegate协议。在ViewController类下面,创建一个ViewController类扩展来实现协议:
[AppleScript] 纯文本查看 复制代码
extension ViewController: ARSCNViewDelegate {

}


在类扩展的内部,实现renderer(_:didAdd:for:)方法:
[AppleScript] 纯文本查看 复制代码
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {

}

每当场景视图的session添加了一个新的ARAnchor时,就会调用这个协议方法。ARAnchor是表示三维空间中物理位置和方向的对象。稍后我们将使用ARAnchor来检测水平面。

将sceneView的委托分配给setUpSceneView()中的ViewController。

还可以设置sceneView的调试选项来显示现实环境中的特征点。这可以帮助找到一个特征点足够多的水平面。一旦检测到足够的特征点来识别水平表面,就会调用renderer(_:didAdd:for:)。

目前setUpSceneView()方法应该是这样的:
[AppleScript] 纯文本查看 复制代码
func setUpSceneView() {
    let configuration = ARWorldTrackingConfiguration()
    configuration.planeDetection = .horizontal
    
    sceneView.session.run(configuration)
    
    sceneView.delegate = self
    sceneView.debugOptions = [ARSCNDebugOptions.showFeaturePoints]
}

更新renderer(_:didAdd:for:)方法:
[AppleScript] 纯文本查看 复制代码
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
    // 1
    guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
    
    // 2
    let width = CGFloat(planeAnchor.extent.x)
    let height = CGFloat(planeAnchor.extent.z)
    let plane = SCNPlane(width: width, height: height)
    
    // 3
    plane.materials.first?.diffuse.contents = UIColor.transparentLightBlue
    
    // 4
    let planeNode = SCNNode(geometry: plane)
    
    // 5
    let x = CGFloat(planeAnchor.center.x)
    let y = CGFloat(planeAnchor.center.y)
    let z = CGFloat(planeAnchor.center.z)
    planeNode.position = SCNVector3(x,y,z)
    planeNode.eulerAngles.x = -.pi / 2
    
    // 6
    node.addChildNode(planeNode)
}

创建一个SCNPlane来可视化ARPlaneAnchor。SCNPlane是一个矩形的“单边”平面几何。我们获取未包装的ARPlaneAnchor区段的x和z属性,并使用它们创建一个SCNPlane。
使用刚刚创建的SCNPlane几何图形初始化一个SCNNode。
初始化x、y和z常量来表示planeAnchor的中心x、y和z位置。这是我们的planeNode的位置。
最后,将planeNode作为子节点添加到新添加的SceneKit节点上。

然后Build项目,效果如下图:

随着ARKit接收到关于我们的环境的额外信息,我们需要扩展之前检测到的水平面,以利用更大的表面,或者用新的环境信息更准确地表示,实现方法renderer(_:didUpdate:for:):
[AppleScript] 纯文本查看 复制代码
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {

}

每次更新SceneKit节点的属性以匹配其对应的锚(anchor)时,都会调用此方法。

node参数为我们提供了锚的更新位置。锚参数给出了更新后的宽度和高度。有了这两个参数,我们可以更新之前实现的SCNPlane,用更新后的宽度和高度反映新的位置。

接下来,在renderer(_:didUpdate:for:)中添加以下代码:
[AppleScript] 纯文本查看 复制代码
// 1
guard let planeAnchor = anchor as?  ARPlaneAnchor,
    let planeNode = node.childNodes.first,
    let plane = planeNode.geometry as? SCNPlane
    else { return }

// 2
let width = CGFloat(planeAnchor.extent.x)
let height = CGFloat(planeAnchor.extent.z)
plane.width = width
plane.height = height

// 3
let x = CGFloat(planeAnchor.center.x)
let y = CGFloat(planeAnchor.center.y)
let z = CGFloat(planeAnchor.center.z)
planeNode.position = SCNVector3(x, y, z)

Build应用,实现效果如下图:


Step 2: 添加3D物体

在本部分教程刚开始的时候下载的project包含了一个3D物体(ship)。
在ViewController类中插入以下方法,将船放置在水平面的顶部:
[AppleScript] 纯文本查看 复制代码
@objc func addShipToSceneView(withGestureRecognizer recognizer: UIGestureRecognizer) {
    let tapLocation = recognizer.location(in: sceneView)
    let hitTestResults = sceneView.hitTest(tapLocation, types: .existingPlaneUsingExtent)
    
    guard let hitTestResult = hitTestResults.first else { return }
    let translation = hitTestResult.worldTransform.translation
    let x = translation.x
    let y = translation.y
    let z = translation.z
    
    guard let shipScene = SCNScene(named: "ship.scn"),
        let shipNode = shipScene.rootNode.childNode(withName: "ship", recursively: false)
        else { return }
    
    
    shipNode.position = SCNVector3(x,y,z)
    sceneView.scene.rootNode.addChildNode(shipNode)
}

对于这部分内容不熟悉的可以查看之前的文章:
这里与之前唯一的区别是,我们在types参数中传递了一个不同的参数来检测sceneView中现有的平面锚。
[AppleScript] 纯文本查看 复制代码
func addTapGestureToSceneView() {
    let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.addShipToSceneView(withGestureRecognizer:)))
    sceneView.addGestureRecognizer(tapGestureRecognizer)
}

该方法将向sceneView添加一个tap手势识别器。

对于顶部的cherry,调用viewDidLoad()内部方法,向sceneView添加一个tap手势识别器:
[AppleScript] 纯文本查看 复制代码
addTapGestureToSceneView()

现在运行,你应该能够检测到一个可视化的水平面,然后上面会放置一个小船,你可以任意角度查看它。效果如下图:


可以通过取消viewDidLoad()内部的configureLighting()注释来启用照明。该功能非常简单,用两行代码来启用照明:
[AppleScript] 纯文本查看 复制代码
sceneView.autoenablesDefaultLighting = true
sceneView.automaticallyUpdatesLighting = true


完整项目链接:https://github.com/appcoda/ARKitHorizontalPlaneDemo
参考链接:https://www.appcoda.com/arkit-horizontal-plane/


------AR Portal(AR开发者社区)整理
关注微信公众号:AR开发者社区(国内领先的AR开发者交流学习社区和AR内容平台

排名
2297
昨日变化

37

主题

265

帖子

1535

积分

Rank: 9Rank: 9Rank: 9

UID
156756
好友
11
蛮牛币
1007
威望
0
注册时间
2016-7-13
在线时间
539 小时
最后登录
2020-3-31

专栏作家

沙发
楼主 2019-10-21 21:02:35 只看该作者
更多开发干货,可以关注微信公众号:AR开发者社区
5熟悉之中
922/1000
排名
4592
昨日变化

3

主题

111

帖子

922

积分

Rank: 5Rank: 5

UID
56032
好友
2
蛮牛币
1641
威望
0
注册时间
2014-11-18
在线时间
491 小时
最后登录
2020-5-22
板凳
2019-10-25 14:31:21 只看该作者
这个说基于什么引擎开发的呀,unity ARKit 怎么下架了,在Asset Store 上搜索不到了
排名
64942
昨日变化

0

主题

12

帖子

17

积分

Rank: 1

UID
169072
好友
0
蛮牛币
25
威望
0
注册时间
2016-9-18
在线时间
6 小时
最后登录
2020-4-30
地板
2019-11-28 16:39:53 只看该作者
谢谢分享!!!!!!!

0

主题

12

帖子

16

积分

Rank: 1

UID
337346
好友
0
蛮牛币
10
威望
0
注册时间
2019-12-7
在线时间
4 小时
最后登录
2019-12-20
5#
2019-12-7 11:54:13 只看该作者
谢谢分享!!
排名
64942
昨日变化

0

主题

17

帖子

59

积分

Rank: 2Rank: 2

UID
221022
好友
0
蛮牛币
10
威望
0
注册时间
2017-5-7
在线时间
40 小时
最后登录
2020-5-6
6#
2019-12-24 10:10:44 只看该作者
谢谢分享!!!!!!!
您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则

,