三、ARKit涂鸦①

欢迎小伙伴们的到来~

(一)介绍

如果小伙伴们还没对ARKit有任何的了解可以先看看我前面两章的介绍再来更好的看本章内容

一、ARKit初探索

二、SCNGeometry代码创建几何体

本次的代码已上传 github

先看下我本次分享东西的效果图

1.gif

效果图

(二)理论知识~

下面我们需要对几个概念和类有初步的认识
因为篇幅过长我分到另一篇文章中了,点击查看ARKit代理相关类

(三)开始~

我们要实现在AR场景中绘图,大致我们需要以下几个步骤:

1.检测平面,提示用户在平面内绘图(我们绘制的图像需要放到一个平面上这样看起来会更加真实一些)

2.点击屏幕某个点的获取真实世界对象的位置,如果在一个平面内将手指划过的位置绘制在真实世界中,并实现可以继续绘制某条已经停止绘制的线

3.通过双指向上的手势变成一个有高度的面

下面我们就来实现所有的步骤:

1. 检测平面

我们这里检测平面并用orange颜色的矩形表示出来平面范围

关键代码

//①开启水平面检测并配置到会话
//会话配置开启水平的平面检测(planeDetection默认情况下平面检测关闭的)
let standardConfiguration: ARWorldTrackingConfiguration = {
        let configuration = ARWorldTrackingConfiguration()
        configuration.planeDetection = .horizontal
        return configuration
}()
//用配置启动会话
override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        sceneView.session.run(self.standardConfiguration)
}

//②使用ARSCNViewDelegate检测平面
//一个与新的AR锚点相对应的SceneKit节点已添加到场景中。
/*根据会话配置,ARKit可以自动向会话添加锚点。该视图为每个新锚点调用此方法一次。ARKit还会为调用add(anchor:)方法为ARAnchor使用会话的方法手动添加的任何对象提供可视内容。您可以通过将几何(或其他SceneKit功能)附加到此节点或添加子节点来为锚点提供可视内容。或者,您可以实现renderer(_:nodeFor:)SCNNode方法来为锚点创建自己的节点(或子类的实例)。*/
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {

    if anchor is ARPlaneAnchor {

     //获取ARPlaneAnchor
        let planeAnchor = anchor as! ARPlaneAnchor
        /*当ARKit首先检测到一个ARPlaneAnchor平面时,产生的对象具有一个center值(0,0,0),表示其值的transform位于平面的中心点。
        随着场景分析和平面检测的继续,ARKit可能会确定先前检测到的平面锚点是更大的现实世界表面的一部分,从而增加extent宽度和长度值。平面的新边界可能不会围绕其初始位置对称,所以center点相对于锚(未更改)transform矩阵而改变
        虽然此属性的类型为vector_float3,但平面锚总是二维的,并且总是相对于其transform位置仅在x和z方向上定位和定尺寸。(即,该向量的y分量始终为零)*/
        let extent = planeAnchor.extent//估计的检测平面的宽度和长度
        let center = planeAnchor.center
        //let transform = planeAnchor.transform

        //添加平面node
        let w_2 = extent.x / 2.0
        let h_2 = extent.z / 2.0
        let lineSource = SCNGeometrySource(vertices:
            [SCNVector3Make(-w_2, 0,  h_2),
            SCNVector3Make( w_2, 0,  h_2),
            SCNVector3Make(-w_2, 0, -h_2),
            SCNVector3Make( w_2, 0, -h_2)])
        let indices:[UInt32] = [0, 1, 1, 3, 3, 2, 2, 0]
        let lineElements = SCNGeometryElement(indices: indices, primitiveType: .line)
        let line = SCNGeometry(sources: [lineSource], elements: [lineElements])
        //渲染器
        line.firstMaterial = SCNMaterial()
         line.firstMaterial?.diffuse.contents = UIColor.orange

        let planeNode = SCNNode(geometry: line)
        planeNode.position = SCNVector3(center)
//      planeNode.transform = SCNMatrix4(transform)

        node.addChildNode(planeNode)
        }
    }

我们使用ARSCNViewDelegate检测锚点的方法让ARSCNView帮我们找到平面锚点,然后我们使用画线的方法画了一个矩形来表示检测到的平面范围,以提示用户在此范围内进行绘画

2.在平面上画线

实现步骤

①获取用户触摸事件,并获取到用户本次滑动的两点位置

guard let firstTouch = touches.first else {
                return;
}
let location = firstTouch.location(in: self.sceneView)
let pLocation = firstTouch.previousLocation(in: self.sceneView)

②使用sceneView hitTest(_ point: CGPoint,
options: [SCNHitTestOption : Any]? = nil) -> [SCNHitTestResult] 函数搜索场景中与屏幕上点对应的坐标。


这个方法也可以了解一下 func hitTest(_ point: CGPoint,
types: ARHitTestResult.ResultType) -> [ARHitTestResult]

搜索对应于SceneKit视图中某个点真实世界的对象或AR锚点。

//            ARHitTestResult.ResultType:
//            featurePoint 由ARKit自动识别的点是连续表面的一部分,但没有相应的锚点。
//            estimatedHorizontalPlane 通过搜索(没有相应的锚)检测到的现实平面,其方向垂直于重力。
//            existingPlane 已经在场景中的平面锚(用planeDetection选项检测到),而不考虑平面的大小。
//            existingPlaneUsingExtent 已经在场景中的平面锚(用planeDetection选项检测到),考虑平面的有限大小。
//注:这里我使用existingPlaneUsingExtent为了确定点在一个平面,我只允许在平面上涂鸦
 let planeHitTestResults = self.sceneView?.hitTest(point, types: .existingPlaneUsingExtent)
if let result = planeHitTestResults?.first {
            
    let translation = result.worldTransform.columns.3
    planeHitTestPosition = SCNVector3Make(translation.x, translation.y, translation.z)
}

③然后我们把获取的真实世界的两个点的坐标添加到数组中存储并进行绘制

具体查看上传的项目中MQShapeNode addVertices和updateDrawing方法

let source = SCNGeometrySource(vertices:self._lineVertices)
            let elements = SCNGeometryElement(indices: self._lineIndices, primitiveType: .line)
            let geometry = SCNGeometry(sources: [source], elements: [elements])
            
            //渲染器
            geometry.firstMaterial = SCNMaterial()
            geometry.firstMaterial?.diffuse.contents = UIColor.red
            
            self.geometry = geometry

2.升高面

将绘制的线通过双指向上的手势变成一个有高度的面

利用线的索引数组和当前需要调整的面的高度来添加面的顶点和面的索引, 比如 有 线段 [(0, 0, 0),(1, 0, 0), (2,0,0), (10,0,2), (10,0,3)] 线段索引为[0,1, 1,2, 3,4]。我们两两分割来遍历线段索引, 开始的0,1索引要为面添加四个顶点,而“1,2”因为“1”点的顶点已经在“0,1”时候添加了所以本次添加两个顶点,“3,4”因为“3”顶点没有和“2”顶点相连接所以是新的两个点要为面添加四个顶点

关键代码,具体查看项目updateDrawing方法

//新线段添加四个顶点信息,旧线段添加两个顶点信息
            var i = 0
            let lineIndicesCount = self._lineIndices.count
            while i+1 < lineIndicesCount {
                
                let firstIndice = Int(self._lineIndices[i])
                let secondIndice = Int(self._lineIndices[i+1])
                //是否是一条新的线段
                let isNewLine = (i-1 > 0 && firstIndice == self._lineIndices[i-1]) ? false : true
                
                if isNewLine {
                    
                    let count = UInt32(self._planeVertices.count)
                    //顶点索引,我这里逆向遍历顶点,两个三角形拼合一个矩形
                    /* 顶点添加顺序1 2
                                 0 3 方便下次有重复点时直接取用 2,3
                     */
                    self._planeIndices += [0+count, 2+count, 1+count,
                                           1+count, 2+count, 3+count]
                    
                    //四个顶点
                    let firstVertice = self._lineVertices[firstIndice]
                    let secondVertice = self._lineVertices[secondIndice]
                    
                    self._planeVertices += [firstVertice,
                                            SCNVector3Make(firstVertice.x, firstVertice.y+self._height, firstVertice.z),
                                            secondVertice,
                                            SCNVector3Make(secondVertice.x, secondVertice.y+self._height, secondVertice.z)]
                } else {
                    
                    let count = UInt32(self._planeVertices.count-2)
                    //顶点索引
                    self._planeIndices += [0+count, 2+count, 1+count,
                                           1+count, 2+count, 3+count]
                    
                    //添加新的两个顶点
                    let secondVertice = self._lineVertices[secondIndice]
                    self._planeVertices += [secondVertice,
                                            SCNVector3Make(secondVertice.x, secondVertice.y+self._height, secondVertice.z)]
                }
                
                i += 2
            }

本章内容就到这里感谢小伙伴们的到来~
代码已经上传github

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

推荐阅读更多精彩内容