相關(guān)工程地址:https://github.com/markdaws/arkit-by-example
在上一篇文章中我們用ARKit檢測(cè)了真實(shí)世界的水平面,然后將檢測(cè)到的平面進(jìn)行了可視化。在這篇文章中我們會(huì)添加一些虛擬的元素到我們的AR體驗(yàn)中然后開(kāi)始與我們檢測(cè)到的平面進(jìn)行交互
讀完整篇文章以后我們可以丟立方體到這個(gè)虛擬的世界了,把真實(shí)的物理效果應(yīng)用到立方體上他們互相之間就有了交互,就會(huì)有輕微的碰撞并且導(dǎo)致立方體被碰到一邊
射線檢測(cè)
你在第一個(gè)教程的時(shí)候就了解到我們可以在任意的XYZ位置添加虛擬的3D內(nèi)容,然后他會(huì)在真實(shí)的世界中被渲染和追蹤?,F(xiàn)在我們有了平面檢測(cè),所以我們就想要添加一些可以和這些平面交互的舞臺(tái)。這可以讓你的應(yīng)用看起來(lái)像你放置了物體在你的桌子椅子或者地板上
在這個(gè)應(yīng)用中,當(dāng)用戶點(diǎn)擊屏幕的時(shí)候,我們做了一次射線檢測(cè),這包含了獲取2D屏幕坐標(biāo)然后從攝像頭的位置穿過(guò)2D屏幕上的點(diǎn)發(fā)出一道射線然后進(jìn)入場(chǎng)景中。如果這個(gè)射線接觸到任何一個(gè)平面我們就獲得了一個(gè)射線檢測(cè)的結(jié)果,我們就可以拿到這個(gè)射線和平面的交點(diǎn)坐標(biāo)放置我們的物體
代碼是很簡(jiǎn)單的,ARSCNView包含有一個(gè)hitTest方法,你只需要傳入一個(gè)屏幕的坐標(biāo),他就會(huì)從攝像頭的起點(diǎn)到那個(gè)點(diǎn)投影一個(gè)3D的射線然后返回檢測(cè)的結(jié)果:
- (void)handleTapFrom: (UITapGestureRecognizer *)recognizer {
// Take the screen space tap coordinates and pass them to the
// hitTest method on the ARSCNView instance
CGPoint tapPoint = [recognizer locationInView:self.sceneView];
NSArray<ARHitTestResult *> *result = [self.sceneView hitTest:tapPoint types:ARHitTestResultTypeExistingPlaneUsingExtent];
// If the intersection ray passes through any plane geometry they
// will be returned, with the planes ordered by distance
// from the camera
if (result.count == 0) {
return;
}
// If there are multiple hits, just pick the closest plane
ARHitTestResult * hitResult = [result firstObject];
[self insertGeometry:hitResult];
}
得到了ARHitTestResult,我們就可以取得射線和平面的交點(diǎn)的世界坐標(biāo),然后往那個(gè)坐標(biāo)上放置一些虛擬的物體。在這篇文章中我們只會(huì)放置一個(gè)簡(jiǎn)單的立方體,之后我們會(huì)讓他看的更加真實(shí)
- (void)insertGeometry:(ARHitTestResult *)hitResult {
float dimension = 0.1;
SCNBox *cube = [SCNBox boxWithWidth:dimension
height:dimension
length:dimension
chamferRadius:0];
SCNNode *node = [SCNNode nodeWithGeometry:cube];
// The physicsBody tells SceneKit this geometry should be
// manipulated by the physics engine
node.physicsBody = [SCNPhysicsBody
bodyWithType:SCNPhysicsBodyTypeDynamic
shape:nil];
node.physicsBody.mass = 2.0;
node.physicsBody.categoryBitMask = CollisionCategoryCube;
// We insert the geometry slightly above the point the user tapped
// so that it drops onto the plane using the physics engine
float insertionYOffset = 0.5;
node.position = SCNVector3Make(
hitResult.worldTransform.columns[3].x,
hitResult.worldTransform.columns[3].y + insertionYOffset,
hitResult.worldTransform.columns[3].z
);
// Add the cube to the scene
[self.sceneView.scene.rootNode addChildNode:node];
// Add the cube to an internal list for book-keeping
[self.boxes addObject:node];
}
添加物理效果
AR是用來(lái)增強(qiáng)現(xiàn)實(shí)世界的,所以為了讓我們的物體變得更加真實(shí),我們可以加一些物理效果讓他有些重量的感覺(jué)
從上面的代碼中我們可以看到我們給每一個(gè)立方體一個(gè)physicsBody,這表明這個(gè)幾何體是受到SceneKit的物理引擎控制的。我們同樣給每一個(gè)Plane一個(gè)physicsBody,所以他們兩個(gè)就可以進(jìn)行交互了
停止平面檢測(cè)
一旦我們檢測(cè)完了我們的環(huán)境然后得到了一定數(shù)量的平面,我們就不需要ARKit持續(xù)的給我們新的平面并且很可能更新現(xiàn)有的平面,這樣會(huì)影響到我們已經(jīng)添加到場(chǎng)景當(dāng)中的物體
在這個(gè)應(yīng)用中如果用戶按下兩個(gè)手指并維持超過(guò)一秒,我們會(huì)隱藏所有的平面然后關(guān)閉平面檢測(cè)。要做到這個(gè)只需要更新ARSession設(shè)置的planeDetection屬性,然后重新跑這個(gè)會(huì)話。默認(rèn)的這個(gè)會(huì)話會(huì)保持同樣的坐標(biāo)系以及我們一經(jīng)發(fā)現(xiàn)的所有anchor:
// Get our existing session configuration
ARWorldTrackingSessionConfiguration *configuration = (ARWorldTrackingSessionConfiguration *)self.sceneView.session.configuration;
// Turn off future plane detection and updating
configuration.planeDetection = ARPlaneDetectionNone;
// Re-run the session for the changes to take effect
[self.sceneView.session runWithConfiguration:configuration];