将3D对象与estametedVerticalPlane

时间:2019-05-21 20:54:38

标签: ios scenekit arkit

我有这本书,但是我正在从AR / VR周免费的视频教程中重新混合家具应用程序。

我想让3D墙面画布与检测到的墙/垂直平面对齐。

事实证明这比我想的要难。定位不是问题。与家具放置应用程序非常相似,您只需获取hittest.worldtransform的column3并为该向量3提供新的几何体位置。

但是我不知道我该怎么做才能使3D对象旋转到在对齐的检测平面上面向前方。因为我有一个画布对象,所以照片在画布的一侧。在放置时,照片始终朝外。

我考虑过对画布进行任意旋转以使其面向前方,但是那只有在我向北看并将画布放在我右边的墙上时才是正确的。

我已经在线尝试了很多解决方案,但其中一个始终使用.existingPlaneUsingExtent。用于垂直平面检测。这使您可以从以下位置获取ARPlaneAnchor: hittest.anchor? as ARPlaneAnchor。 如果在使用.estimatedVerticalPlane anchor时尝试这样做?是nil

当我的水平3D对象开始悬空时,我也没有继续沿着这条路线走。这可能取决于控制流逻辑,但在垂直画布放置起作用之前,我一直忽略它。

我当前的思路是获取画布的前向矢量,并将其向着检测到的UIImage或命中点的垂直平面的前向矢量旋转。

如何从3D点获得前向矢量。还是从网格图像中获得前向量,即UIImage检测到垂直墙时将ARKit放置为叠加层?

这里是一个例子。画布显示画布的背面,并且与检测到的垂直列(即列)不平行。但是有一个“将海报放置在此处”网格,这是我希望画布与之对齐并能够看到照片的地方。

canvasback

我尝试过的事情。 使用.estimatedVerticalPlane ARKit estimatedVerticalPlane hit test get plane rotation

我不知道如何正确应用该矩阵和SO答案中的欧拉角结果。

我添加图片功能。

func addPicture(hitTestResult: ARHitTestResult) {
    // I would like to convert estimate hitTest to a anchorpoint
    // it is easier to rotate a node to a anchorpoint over calculating eularAngles
    // we have all detected anchors in the _Renderer SCNNode. however there are

    // Get the current furniture item, correct its position if necessary,
    // and add it to the scene.
    let picture = pictureSettings.currentPicturePiece()

    //look for the vertical node geometry in verticalAnchors
    if let hitPlaneAnchor = hitTestResult.anchor as? ARPlaneAnchor {
      if let anchoredNode = verticalAnchors[hitPlaneAnchor]{
        //code removed as a .estimatedVerticalPlane hittestResult doesn't get here
      }
    }else{
      // Transform hitresult to world coords
      let worldTransform = hitTestResult.worldTransform
      let anchoredNodeOrientation = worldTransform.eulerAngles
        picture.rotation.y =
          -.pi * anchoredNodeOrientation.y
        //set the transform matirs
        let positionMatris = worldTransform.columns.3
        let position = SCNVector3 (
          positionMatris.x,
          positionMatris.y,
          positionMatris.z
        )
        picture.position = position + pictureSettings.currentPictureOffset();

    }
    //parented to rootNode of the scene
    sceneView.scene.rootNode.addChildNode(picture)
  }

感谢任何可用的帮助。

编辑: 我注意到“手形”或3D模型不正确/相反? 正Z指向左侧,正X面向相机,因为我期望的是模型的正面。这是问题吗?

1 个答案:

答案 0 :(得分:0)

您应该避免使用世界坐标将节点直接添加到场景中。相反,您应该通过添加ARSession来通知ARAnchor感兴趣的区域,然后使用会话回调为添加的锚点出售SCNNode

例如,您的点击测试可能类似于:

@objc func tapped(_ sender: UITapGestureRecognizer) {
    let location = sender.location(in: sender.view)
    guard let hitTestResult = sceneView.hitTest(location, types: [.existingPlaneUsingGeometry, .estimatedVerticalPlane]).first,
          let planeAnchor = hitTestResult.anchor as? ARPlaneAnchor,
          planeAnchor.alignment == .vertical else { return }
    let anchor = ARAnchor(transform: hitTestResult.worldTransform)
    sceneView.session.add(anchor: anchor)
}

此处识别出的轻击手势用于检测ARSCNView中的轻击。当检测到轻击时,执行命中测试以查找现有平面和估计平面。如果飞机是垂直的,我们在命中测试结果的ARAnchor上添加worldTransform,并将锚点添加到ARSession。这会将该点注册为ARSession的关注区域,因此在其中添加内容后,我们将获得更好的跟踪和更少的漂移。

接下来,我们需要为新添加的SCNNode出售ARAnchor。例如

func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
    if anchor is ARPlaneAnchor {
        let anchorNode = SCNNode()
        anchorNode.name = "anchor"
        return anchorNode
    } else {
        let plane = SCNPlane(width: 0.67, height: 1.0)
        plane.firstMaterial?.diffuse.contents = UIImage(named: "monaLisa")
        let planeNode = SCNNode(geometry: plane)
        planeNode.eulerAngles = SCNVector3(CGFloat.pi * -0.5, 0.0, 0.0)
        let node = SCNNode()
        node.addChildNode(planeNode)
        return node
    }
}

在这里,我们首先检查锚点是否为ARPlaneAnchor。如果是这样,我们将出售一个空节点用于调试。如果不是,则是命中测试结果中添加的锚。因此,我们为最近的拍子创建了几何图形和节点。因为它是一个垂直平面并且我们的内容平放,所以需要绕x轴旋转它。因此,我们将其调整为eulerAngles以使其直立。如果我们要返回planeNode,则将直接删除对eulerAngles的调整,因此我们将其添加为空节点的子节点并返回。

应该导致类似以下的情况。

Screencast