平面检测-搜索真实世界的表面

现在我们已经完成了正确运行ARKit项目的所有基本设置,我们希望我们的设备能够坐在水平表面上。这是飞机检测。在本节中,我们将学习如何激活平面检测。我们将熟悉锚点以及如何使用它们将对象放置在锚点上。此外,我们将能够在现实生活中看到我们发现的飞机锚。从现在开始,我们将更多地投入到代码中。

下载

要学习本教程,您需要Xcode 10或更高版本,以及来自Configuration for ARKit的最终Xcode项目。您可以下载本节的最终Xcode项目,以帮助您与自己的进度进行比较。

水平平面检测

首先,我们需要打开配置的平面检测属性并将其设置为水平,以检测平面(如地板或桌子)。在配置声明下面写:

configuration.planeDetection = .horizontal

ARSCNViewDelegate

ViewController类中,添加了一个委托ARSCNViewDelegate,以允许视图在渲染场景时接收信息。ARSCNViewDelegate是一种协议,它包含许多方法来帮助跟踪摄像机视图中的对象。方法就像程序或例程来实现某些东西。

在编程中,委托是一种设计模式,允许类将其职责委托给另一个对象。换句话说,就像要求别人为你做一份工作。在我们的例子中,ViewController将自己指定为ARSCNView的委托,委托者,从场景视图中检索内容的任务,管理其更新并处理其事件。

sceneView.delegate = self

一旦执行了任务,代表就会将信息报告回场景视图。

为了更多地了解Swift中的委派,我邀请您访问或查看本书第4章中的委托部分。

扩展课程

为了保持井井有条,让我们创建一个新文件来托管与ARSCNViewDelegate相关的所有代码。右键单击ViewController.swift并选择新建文件...。然后,在Source下选择Swift File,点击Next。将其命名为ViewController + ARSCNViewDelegate,然后命名为Create

导入套件

一旦创建了新的Swift文件ViewController + ARSCNViewDelegate.swift,就会自动导入Foundation框架。它是我们不需要的应用程序的基础框架。请改为使用以下框架替换它。

import SceneKit
import ARKit

延期

这个文件将作为ViewController类的扩展,这里的代码将成为该类的一部分。为表明这一意图,请写下:

extension ViewController: ARSCNViewDelegate {
}

将显示错误消息:“ViewController”与协议“ARSCNViewDelegate”的冗余一致性。那是因为我们已经在同一个类中采用了ARSCNViewDelegate。在ViewController.swift文件中,将其删除。当我们在它时,向下滚动并删除Mark下的注释掉的代码,这是该协议下的一个方法的给定示例。Mark帮助我们分离文件中的代码段。

添加锚点

让我们回到ViewController + ARSCNViewDelegate.swift。现在,让我们从ARSCNViewDelegate实现一个新方法来查找表面。键入didAdd并在选项中选择渲染器。该didAdd方法当相机检测到物体会通知我们,然后标记的它。一个是类型的ARAnchor给出关于跟踪的对象的位置,取向和尺寸信息。ARAnchor有意用于在场景上放置虚拟对象。然后为该锚分配一个简称为节点SCNNode

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
}

平面锚点

我们不想要任何对象,更具体地说我们想要搜索平面。如果跟踪的对象是平面,让我们进行场景测试。从技术上讲,如果锚是一个ARPlaneAnchor

if anchor is ARPlaneAnchor {
    print("Horizontal surface detected")
} else {
    return
}

如果确实如此,请在控制台“检测到水平表面”中打印以告知我们。否则,返回或退出方法。运行该应用程序以测试它。

返回委托文件,为planeAnchor声明一个常量。我们将使用它作为锚点来放置对象。

let planeAnchor = anchor as! ARPlaneAnchor

这意味着如果是平面,则将其类型转换为平面锚

平面节点

当我们运行应用程序时,我们可以在调试区域中看到找到水平表面时。但是在屏幕上看到它不是很好吗?为此,我们将添加一个函数来创建一个节点作为我们的视觉辅助。

func createPlane(planeAnchor: ARPlaneAnchor) -> SCNNode {}

该函数有一个名为参数planeAnchorARPlaneAnchor就像在不断didAdd方法。该函数将返回一个SCNNode,如右箭头所示。所以基本上,它输入一个平面锚并输出一个节点。

你应该在一个函数中错误地返回一个预期返回'SCNNode'的函数中的Missing return。不要担心,我们将继续编写代码并在最后添加缺少的返回值。

平面几何

在此函数中,我们将为节点设置几何,并且该几何是平面。因此,使用其范围属性创建一个大小为planeAnchor的平面。

let plane = SCNPlane(width: CGFloat(planeAnchor.extent.x), height: CGFloat(planeAnchor.extent.z))

在这里,我们假设高度将被指定为y的范围。但是你看文档,y向量不存在,而z是要使用的。

您应该看到推荐的修复程序出现错误。只需单击Fix即可将Float类型的x extent值转换为CGFloat。为y做同样的事情。

接下来,让我们为飞机赋予纹理。我们将使用网格图像。

plane.firstMaterial?.diffuse.contents = UIImage(named: "grid")

然后,使用平面的几何创建一个名为planeNode的节点。

let planeNode = SCNNode(geometry: plane)

到现在为止,您应该已经识别出材料漫反射这两个词。你在Scene Editor中看过它。您现在正在学习如何在代码中应用它。

飞机位置

所以,就像我们为手表所做的步骤一样,我们需要定位它。将平面节点放在检测到的曲面的中心

planeNode.position = SCNVector3(planeAnchor.center.x, planeAnchor.center.y, planeAnchor.center.z)

最后,此函数需要在调用时返回一个值。

return planeNode

添加平面节点

回到didAdd方法,让我们调用该函数。

let planeNode = createPlane(planeAnchor: planeAnchor)

然后,将planeNode作为表示平面的节点的子节点。

node.addChildNode(planeNode)

运行应用程序以查看网格。

修复平面节点

检查网格时,您应该会看到一些问题。首先,网格是立起来的,另一个问题是你只能看到飞机一侧的网格。这是我们需要解决的两个问题。

好吧,还记得在我们第一次拖动飞机作为屏幕时的手表场景吗?它的默认方向是垂直的。嗯,这里也是如此。所以我们需要将它旋转90度。但是,Swift将角度存储在弧度中。如何将度数转换为弧度?我们应该回顾一下我们的高中数学。为了找到弧度的等价物,这里是等式。

根据图表,你会得到90度是pi的一半。在函数createPlane中,我们将在x轴上旋转网格以使其成为水平。也要顺时针旋转,在前面添加一个减号。

planeNode.eulerAngles.x = -.pi / 2

但严重的是,谁有时间甚至想要计算和转换度数和弧度?幸运的是,Swift有一个功能,GLKMathDegreesToRadians,所以利用它。注释掉前一行代码并替换为此代码。

planeNode.eulerAngles.x = GLKMathDegreesToRadians(-90)

此外,使网格图像覆盖平面的两侧以解决第二个问题。

plane.firstMaterial?.isDoubleSided = true

运行该应用程序以测试修复程序。因此,我们能够在检测到表面时将其可视化,在我的示例中是地板。但我们知道地板比那更大。不幸的是,当我四处走动时,网格并没有变大。

公式和图表

重构控制流程

在我们继续之前,我想重构if else语句。有一种更好的方式来编写它。我想从这种方式开始,使其更容易理解。另一种选择是使用guard语句。Guard是另一种类似于if else语句的控制流。它有助于避免开发中的错误,因为它会强制程序在失败的情况下退出。从这开始,我们将在整个课程中使用guard。

替换此代码块:

if anchor is ARPlaneAnchor {
} else {
    return
}

为这行代码:

guard anchor is ARPlaneAnchor else {return}

更新锚点

为了能够更新面锚点的大小,添加didUpdate后方法didAdd之一。

func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {_

我们将采用与以前相同的想法。

guard anchor is ARPlaneAnchor else {return}
print("Horizontal surface updated")

运行应用程序。

与之前相同,我们将宣布一个planeAnchor

let planeAnchor = anchor as! ARPlaneAnchor

更新平面锚点的尺寸的方法,我们首先必须将其从场景中删除,然后将其添加回来。对于的所有子节点的节点,从父节点删除它们。

node.enumerateChildNodes { (childNode, _) in
    childNode.removeFromParentNode()
}

现在将其添加回场景,使用相同的功能创建另一个平面

let planeNode = createPlane(planeAnchor: planeAnchor)
node.addChildNode(planeNode)

再次运行该应用程序。您会看到在移动设备时,表面的大小会相应更新。

删除锚点

有时会发生错误。场景可以检测同一表面的多个锚点。我们可以通过添加didRemove方法来解决这个问题。

func renderer(_ renderer: SCNSceneRenderer, didRemove node: SCNNode, for anchor: ARAnchor) {

然后,通过应用与之前相同的代码来删除平面锚点。

guard anchor is ARPlaneAnchor else {return}
print("Horizontal surface removed")
        
node.enumerateChildNodes { (childNode, _) in
childNode.removeFromParentNode()

结论

当我们上次预览我们导入的模型时,我们发现它只是漂浮在空中。检测平面锚点是允许我们添加模型,就像它们坐在它们上一样,使其成为更真实的体验。您在本教程中学到的内容不仅可以让您了解如何模拟真实曲面,还可以模拟现实生活中的事件。例如,您可以将物理应用于水平表面以使虚拟对象掉落,在其上驾驶汽车或在场景上为角色设置动画。

与此同时,我希望您能够在场景编辑器中学到的概念代码中受到教育。通过首先在视觉上向您介绍这些概念,我们相信它更容易掌握并且对代码处理不那么持怀疑态度。

原文: https://designcode.io/plane-detection

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

推荐阅读更多精彩内容