三、ARKit涂鴉①

歡迎小伙伴們的到來(lái)~

(一)介紹

如果小伙伴們還沒對(duì)ARKit有任何的了解可以先看看我前面兩章的介紹再來(lái)更好的看本章內(nèi)容

一吸奴、ARKit初探索

二洞坑、SCNGeometry代碼創(chuàng)建幾何體

本次的代碼已上傳 github

先看下我本次分享東西的效果圖

1.gif

效果圖

(二)理論知識(shí)~

下面我們需要對(duì)幾個(gè)概念和類有初步的認(rèn)識(shí)
因?yàn)槠^長(zhǎng)我分到另一篇文章中了,點(diǎn)擊查看ARKit代理相關(guān)類

(三)開始~

我們要實(shí)現(xiàn)在AR場(chǎng)景中繪圖拙毫,大致我們需要以下幾個(gè)步驟:

1.檢測(cè)平面,提示用戶在平面內(nèi)繪圖(我們繪制的圖像需要放到一個(gè)平面上這樣看起來(lái)會(huì)更加真實(shí)一些)

2.點(diǎn)擊屏幕某個(gè)點(diǎn)的獲取真實(shí)世界對(duì)象的位置,如果在一個(gè)平面內(nèi)將手指劃過的位置繪制在真實(shí)世界中,并實(shí)現(xiàn)可以繼續(xù)繪制某條已經(jīng)停止繪制的線

3.通過雙指向上的手勢(shì)變成一個(gè)有高度的面

下面我們就來(lái)實(shí)現(xiàn)所有的步驟:

1. 檢測(cè)平面

我們這里檢測(cè)平面并用orange顏色的矩形表示出來(lái)平面范圍

關(guān)鍵代碼

//①開啟水平面檢測(cè)并配置到會(huì)話
//會(huì)話配置開啟水平的平面檢測(cè)(planeDetection默認(rèn)情況下平面檢測(cè)關(guān)閉的)
let standardConfiguration: ARWorldTrackingConfiguration = {
        let configuration = ARWorldTrackingConfiguration()
        configuration.planeDetection = .horizontal
        return configuration
}()
//用配置啟動(dòng)會(huì)話
override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        sceneView.session.run(self.standardConfiguration)
}

//②使用ARSCNViewDelegate檢測(cè)平面
//一個(gè)與新的AR錨點(diǎn)相對(duì)應(yīng)的SceneKit節(jié)點(diǎn)已添加到場(chǎng)景中依许。
/*根據(jù)會(huì)話配置,ARKit可以自動(dòng)向會(huì)話添加錨點(diǎn)缀蹄。該視圖為每個(gè)新錨點(diǎn)調(diào)用此方法一次峭跳。ARKit還會(huì)為調(diào)用add(anchor:)方法為ARAnchor使用會(huì)話的方法手動(dòng)添加的任何對(duì)象提供可視內(nèi)容。您可以通過將幾何(或其他SceneKit功能)附加到此節(jié)點(diǎn)或添加子節(jié)點(diǎn)來(lái)為錨點(diǎn)提供可視內(nèi)容缺前≈恚或者,您可以實(shí)現(xiàn)renderer(_:nodeFor:)SCNNode方法來(lái)為錨點(diǎn)創(chuàng)建自己的節(jié)點(diǎn)(或子類的實(shí)例)衅码。*/
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {

    if anchor is ARPlaneAnchor {

     //獲取ARPlaneAnchor
        let planeAnchor = anchor as! ARPlaneAnchor
        /*當(dāng)ARKit首先檢測(cè)到一個(gè)ARPlaneAnchor平面時(shí)拯刁,產(chǎn)生的對(duì)象具有一個(gè)center值(0,0,0),表示其值的transform位于平面的中心點(diǎn)逝段。
        隨著場(chǎng)景分析和平面檢測(cè)的繼續(xù)筛璧,ARKit可能會(huì)確定先前檢測(cè)到的平面錨點(diǎn)是更大的現(xiàn)實(shí)世界表面的一部分,從而增加extent寬度和長(zhǎng)度值惹恃。平面的新邊界可能不會(huì)圍繞其初始位置對(duì)稱,所以center點(diǎn)相對(duì)于錨(未更改)transform矩陣而改變
        雖然此屬性的類型為vector_float3棺牧,但平面錨總是二維的巫糙,并且總是相對(duì)于其transform位置僅在x和z方向上定位和定尺寸。(即颊乘,該向量的y分量始終為零)*/
        let extent = planeAnchor.extent//估計(jì)的檢測(cè)平面的寬度和長(zhǎng)度
        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檢測(cè)錨點(diǎn)的方法讓ARSCNView幫我們找到平面錨點(diǎn)参淹,然后我們使用畫線的方法畫了一個(gè)矩形來(lái)表示檢測(cè)到的平面范圍醉锄,以提示用戶在此范圍內(nèi)進(jìn)行繪畫

2.在平面上畫線

實(shí)現(xiàn)步驟

①獲取用戶觸摸事件,并獲取到用戶本次滑動(dòng)的兩點(diǎn)位置

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] 函數(shù)搜索場(chǎng)景中與屏幕上點(diǎn)對(duì)應(yīng)的坐標(biāo)浙值。


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

搜索對(duì)應(yīng)于SceneKit視圖中某個(gè)點(diǎn)真實(shí)世界的對(duì)象或AR錨點(diǎn)恳不。

//            ARHitTestResult.ResultType:
//            featurePoint 由ARKit自動(dòng)識(shí)別的點(diǎn)是連續(xù)表面的一部分,但沒有相應(yīng)的錨點(diǎn)开呐。
//            estimatedHorizontalPlane 通過搜索(沒有相應(yīng)的錨)檢測(cè)到的現(xiàn)實(shí)平面烟勋,其方向垂直于重力。
//            existingPlane 已經(jīng)在場(chǎng)景中的平面錨(用planeDetection選項(xiàng)檢測(cè)到)筐付,而不考慮平面的大小卵惦。
//            existingPlaneUsingExtent 已經(jīng)在場(chǎng)景中的平面錨(用planeDetection選項(xiàng)檢測(cè)到),考慮平面的有限大小瓦戚。
//注:這里我使用existingPlaneUsingExtent為了確定點(diǎn)在一個(gè)平面沮尿,我只允許在平面上涂鴉
 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)
}

③然后我們把獲取的真實(shí)世界的兩個(gè)點(diǎn)的坐標(biāo)添加到數(shù)組中存儲(chǔ)并進(jìn)行繪制

具體查看上傳的項(xiàng)目中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.升高面

將繪制的線通過雙指向上的手勢(shì)變成一個(gè)有高度的面

利用線的索引數(shù)組和當(dāng)前需要調(diào)整的面的高度來(lái)添加面的頂點(diǎn)和面的索引, 比如 有 線段 [(0, 0, 0),(1, 0, 0), (2,0,0), (10,0,2), (10,0,3)] 線段索引為[0,1, 1,2, 3,4]较解。我們兩兩分割來(lái)遍歷線段索引畜疾, 開始的0,1索引要為面添加四個(gè)頂點(diǎn)印衔,而“1啡捶,2”因?yàn)椤?”點(diǎn)的頂點(diǎn)已經(jīng)在“0,1”時(shí)候添加了所以本次添加兩個(gè)頂點(diǎn)当编,“3届慈,4”因?yàn)椤?”頂點(diǎn)沒有和“2”頂點(diǎn)相連接所以是新的兩個(gè)點(diǎn)要為面添加四個(gè)頂點(diǎn)

關(guān)鍵代碼,具體查看項(xiàng)目updateDrawing方法

//新線段添加四個(gè)頂點(diǎn)信息,舊線段添加兩個(gè)頂點(diǎn)信息
            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)
                    //頂點(diǎn)索引忿偷,我這里逆向遍歷頂點(diǎn)金顿,兩個(gè)三角形拼合一個(gè)矩形
                    /* 頂點(diǎn)添加順序1 2
                                 0 3 方便下次有重復(fù)點(diǎn)時(shí)直接取用 2,3
                     */
                    self._planeIndices += [0+count, 2+count, 1+count,
                                           1+count, 2+count, 3+count]
                    
                    //四個(gè)頂點(diǎn)
                    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)
                    //頂點(diǎn)索引
                    self._planeIndices += [0+count, 2+count, 1+count,
                                           1+count, 2+count, 3+count]
                    
                    //添加新的兩個(gè)頂點(diǎn)
                    let secondVertice = self._lineVertices[secondIndice]
                    self._planeVertices += [secondVertice,
                                            SCNVector3Make(secondVertice.x, secondVertice.y+self._height, secondVertice.z)]
                }
                
                i += 2
            }

本章內(nèi)容就到這里感謝小伙伴們的到來(lái)~
代碼已經(jīng)上傳github

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鲤桥,一起剝皮案震驚了整個(gè)濱河市揍拆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌茶凳,老刑警劉巖嫂拴,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異贮喧,居然都是意外死亡筒狠,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門箱沦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)辩恼,“玉大人,你說我怎么就攤上這事≡钜粒” “怎么了疆前?”我有些...
    開封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)聘萨。 經(jīng)常有香客問我竹椒,道長(zhǎng),這世上最難降的妖魔是什么米辐? 我笑而不...
    開封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任胸完,我火速辦了婚禮,結(jié)果婚禮上儡循,老公的妹妹穿的比我還像新娘舶吗。我一直安慰自己,他們只是感情好择膝,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開白布誓琼。 她就那樣靜靜地躺著,像睡著了一般肴捉。 火紅的嫁衣襯著肌膚如雪腹侣。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天齿穗,我揣著相機(jī)與錄音傲隶,去河邊找鬼。 笑死窃页,一個(gè)胖子當(dāng)著我的面吹牛跺株,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播脖卖,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼乒省,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了畦木?” 一聲冷哼從身側(cè)響起袖扛,我...
    開封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎十籍,沒想到半個(gè)月后蛆封,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡勾栗,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年惨篱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片围俘。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡妒蛇,死狀恐怖机断,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情绣夺,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布欢揖,位于F島的核電站陶耍,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏她混。R本人自食惡果不足惜烈钞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望坤按。 院中可真熱鬧毯欣,春花似錦、人聲如沸臭脓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)来累。三九已至砚作,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嘹锁,已是汗流浹背葫录。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留领猾,地道東北人米同。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像摔竿,于是被迫代替她去往敵國(guó)和親面粮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容