ARkit 實(shí)戰(zhàn)教程(Xcode開(kāi)發(fā))
ARKit 最近在開(kāi)發(fā)圈實(shí)在是火,ios 開(kāi)發(fā)者可以快速的進(jìn)行增強(qiáng)現(xiàn)實(shí)的開(kāi)發(fā),簡(jiǎn)化了許多步驟,之前了,我們AR醬也出了相應(yīng)的ARKit 系列教程,今天了算是ARKit 的實(shí)戰(zhàn)教程番外篇,我們實(shí)現(xiàn)一些網(wǎng)上比較流行的一些AR功能。
ps:代碼地址:http://www.arparticles.com/portal.php?mod=view&aid=107
基礎(chǔ):現(xiàn)實(shí)模型
基礎(chǔ)效果演示:
打開(kāi)Xcode,新建一個(gè)AR項(xiàng)目
這次我們用SceneKit 來(lái)渲染3D內(nèi)容。
進(jìn)入項(xiàng)目之后會(huì)有個(gè)smaple project,我們可以嘗試著運(yùn)行:
我們使用SceneKit繪制一個(gè)3D立方體。SceneKit有幾個(gè)基本類,SCNScene是所有3D模型的容器。要向場(chǎng)景添加內(nèi)容,你首先創(chuàng)建幾何,幾何可以是復(fù)雜的形狀,或簡(jiǎn)單的像球體,多維數(shù)據(jù)集,平面等。然后,將幾何模型放在在場(chǎng)景節(jié)點(diǎn)中,并將其添加到場(chǎng)景中。然后,SceneKit將遍歷場(chǎng)景圖并呈現(xiàn)內(nèi)容。
- (void)viewDidLoad {
[super viewDidLoad];
SCNScene *scene = [SCNScene new];
SCNBox *boxGeometry = [SCNBox
boxWithWidth:0.1
height:0.1
length:0.1
chamferRadius:0.0];
SCNNode *boxNode = [SCNNode nodeWithGeometry:boxGeometry];
boxNode.position = SCNVector3Make(0, 0, -0.5);
[scene.rootNode addChildNode: boxNode];
self.sceneView.scene = scene;
}
ARKit和SceneKit的坐標(biāo)系如下所示:
當(dāng)ARSession啟動(dòng)時(shí),計(jì)算出的相機(jī)位置最初設(shè)置為X = 0,Y = 0,Z = 0。
們可以看到立方體的兩面,我們可以稍后添加一些更高級(jí)的照明,但現(xiàn)在我們可以在SCNScene實(shí)例上設(shè)置autoenablesDefaultLighting :
self.sceneView.autoenablesDefaultLighting = YES;
進(jìn)階:平面檢測(cè)+可視化
效果演示:
在我們開(kāi)始之前,將一些調(diào)試信息添加到程序中,即渲染ARKit檢測(cè)到的功能點(diǎn),我們可以打開(kāi)我們的ARSCNView:
self.sceneView.debugOptions =
ARSCNDebugOptionShowWorldOrigin |
ARSCNDebugOptionShowFeaturePoints;
我們來(lái)檢測(cè)平面幾何,在ARKit中,可以通過(guò)設(shè)置planeDetection屬性來(lái)檢測(cè)水平平面。此值可以設(shè)置為ARPlaneDetectionHorizontal或ARPlaneDetectionNone。
- (void)renderer:(id )renderer
didAddNode:(SCNNode *)node
forAnchor:(ARAnchor *)anchor {
SCNNode實(shí)例是ARKit創(chuàng)建的一個(gè)SceneKit節(jié)點(diǎn),它具有一些類似于方向和位置的屬性,然后我們獲得一個(gè)錨實(shí)例,這將告訴我們使用已找到的特定錨點(diǎn)的更多信息,如大小和中心位置的plane。錨實(shí)例實(shí)際上是一個(gè)ARPlaneAnchor類型,比如我們可以得到plane的范圍和中心信息。
我們進(jìn)行渲染plane,可以在虛擬世界中繪制一個(gè)SceneKit 3D平面。為此,我們創(chuàng)建一個(gè)繼承自SCNNode的Plane類。在構(gòu)造方法中,我們創(chuàng)建平面并相應(yīng)地調(diào)整它的大?。?/p>
self.planeGeometry = [SCNPlane planeWithWidth:anchor.extent.x height:anchor.extent.z];
SCNNode *planeNode = [SCNNode nodeWithGeometry:self.planeGeometry];
planeNode.position = SCNVector3Make(anchor.center.x, 0, anchor.center.z);
planeNode.transform = SCNMatrix4MakeRotation(-M_PI / 2.0, 1.0, 0.0, 0.0);
[self addChildNode:planeNode];
現(xiàn)在我們有我們的Plane類,回到ARSCNViewDelegate回調(diào)方法中,當(dāng)ARKit找到一個(gè)新的錨點(diǎn)時(shí),我們可以創(chuàng)建我們的新plane:
if (![anchor isKindOfClass:[ARPlaneAnchor class]]) {
return;
Plane *plane = [[Plane alloc] initWithAnchor: (ARPlaneAnchor *)anchor];
[node addChildNode:plane];
更新Plane SceneKit,使得我們?cè)谝苿?dòng)時(shí)有更穩(wěn)定的效果。
didUpdateNode:(SCNNode *)node
// See if this is a plane we are currently rendering
Plane *plane = [self.planes objectForKey:anchor.identifier];
if (plane == nil) {
[plane update:(ARPlaneAnchor *)anchor];
更新plane的寬度和高度。
- (void)update:(ARPlaneAnchor *)anchor {
self.planeGeometry.width = anchor.extent.x;
self.planeGeometry.height = anchor.extent.z;
self.position = SCNVector3Make(anchor.center.x, 0, anchor.center.z);
運(yùn)行,會(huì)發(fā)現(xiàn)如下一些效果。
添加物理效果
效果預(yù)覽:
在這個(gè)演示中,當(dāng)用戶在屏幕上單擊時(shí),我們執(zhí)行一段代碼,這個(gè)代碼很簡(jiǎn)單,ARSCNView包含一個(gè)hitTest方法,可以通過(guò)獲得的屏幕坐標(biāo)點(diǎn),從相機(jī)中心通過(guò)該點(diǎn)投射一條射線,并返回結(jié)果:
- (void)handleTapFrom: (UITapGestureRecognizer *)recognizer {
CGPoint tapPoint = [recognizer locationInView:self.sceneView];
NSArray *result = [self.sceneView hitTest:tapPoint types:ARHitTestResultTypeExistingPlaneUsingExtent];
if (result.count == 0) {
ARHitTestResult * hitResult = [result firstObject];
[self insertGeometry:hitResult];
通過(guò)上述代碼,我們可以得到射線與平面交叉點(diǎn)的世界坐標(biāo),并在該位置放置一些3D模型等等。
- (void)insertGeometry:(ARHitTestResult *)hitResult {
float dimension = 0.1;
SCNBox *cube = [SCNBox boxWithWidth:dimension
height:dimension
length:dimension
chamferRadius:0];
SCNNode *node = [SCNNode nodeWithGeometry:cube];
node.physicsBody = [SCNPhysicsBody
bodyWithType:SCNPhysicsBodyTypeDynamic
shape:nil];
node.physicsBody.mass = 2.0;
node.physicsBody.categoryBitMask = CollisionCategoryCube;
float insertionYOffset = 0.5;
node.position = SCNVector3Make(
hitResult.worldTransform.columns[3].x,
hitResult.worldTransform.columns[3].y + insertionYOffset,
hitResult.worldTransform.columns[3].z
);
[self.sceneView.scene.rootNode addChildNode:node];
[self.boxes addObject:node];
我們給每個(gè)立方體一個(gè)physicsBody,它是SceneKit的物理引擎。
接下來(lái),我們實(shí)現(xiàn)停止平面檢測(cè)的功能。用戶用兩個(gè)手指按住屏幕1秒鐘,那么我們會(huì)隱藏所有的平面并關(guān)閉平面檢測(cè)。
ARWorldTrackingSessionConfiguration *configuration = (ARWorldTrackingSessionConfiguration *)self.sceneView.session.configuration;
configuration.planeDetection = ARPlaneDetectionNone;
[self.sceneView.session runWithConfiguration:configuration];