在上一篇文章中,我们使用ARKit来检测现实世界中的水平平面,然后将这些平面可视化。在本文中,我们现在将开始为我们的AR体验添加虚拟内容,并开始与检测到的平面进行交互。
到本文结束时,我们将能够将立方体放入世界,将真实物理应用于立方体,以便它们相互作用并产生微型冲击波,使立方体飞得很快。
这是一个显示应用程序运行的视频,您可以看到我们首先如何捕获水平平面,然后我们添加一些3D立方体与场景交互,然后最终导致一些小型爆炸使立方体跳转:
与往常一样,您可以按照以下代码进行操作:https://github.com/markdaws/arkit-by-example/tree/part3
正如您在第一个教程中看到的,我们可以在任何X,Y,Z位置插入虚拟3D内容,它将在现实世界中渲染和跟踪。现在我们有平面检测,我们想要添加与这些平面交互的内容。这将使应用程序看起来像桌子,椅子,地板等顶部有物体。
在这个应用程序中,当用户单击屏幕时,我们执行命中测试,这涉及获取2D屏幕坐标并通过2D屏幕点(在投影平面上具有3D位置)从相机原点发射Ray并进入现场。如果光线与任何平面相交,我们得到命中结果,然后我们获取光线和平面相交的3D坐标,并将我们的内容放置在该3D位置。
这个代码非常简单,ARSCNView包含一个hitTest方法,你传递屏幕坐标,它负责通过相机原点从那个点投射3D光线并返回结果:
- (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,我们可以获得光线/平面交叉发生的世界坐标,并在该位置放置一些虚拟内容。对于本文,我们将只插入一个简单的立方体,稍后我们将使对象看起来更逼真:
- (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假设是为了增强现实世界,所以为了使我们的物体感觉更逼真,我们将添加一些物理来给予一种重量感。
正如您在上面的代码中所看到的,我们为每个立方体提供了一个physicsBody,它指示了SceneKit物理引擎,这个几何应该由物理引擎控制。然后我们还给每个ARKit检测到物理体的平面,以便立方体可以与平面交互(有关更多确切的细节,请参阅github 仓库中的Plane.m类)。
一旦我们绘制了世界并拥有多个平面,我们就不希望ARKit继续为我们提供新的平面并可能更新现有的平面,因为这可能会影响我们已经添加到世界的几何体。
在这个应用程序中,如果用户按住两个手指一秒钟,那么我们隐藏所有平面并关闭平面检测。为此,您需要更新ARSession配置的planeDetection属性并重新运行会话。默认情况下,会话将保持相同的坐标系和找到的任何锚点:
// 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];
在下一篇文章中,我们将向后退一小步,看看我们已编写的一些代码是否有用,添加了一些用于启用/禁用功能的UI控件。我们还将使用光照和纹理来使插入的几何图形看起来更逼真。
原文:https://blog.markdaws.net/arkit-by-example-part-3-adding-geometry-and-physics-fun-8dd2b90037f2 作者:Mark Dawson
本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。
我来说两句