如何使一个SCNNode面向ARAnchor

6

我该如何创建一个指向ARAnchorARNode

我想要使用art.scnassets/ship.scn显示在屏幕中央,并指向我刚刚放置在场景中某个位置的物体。

/// ViewController Class
func placeObject() {
    let screenCentre : CGPoint = CGPoint(x: self.sceneView.bounds.midX, y: self.sceneView.bounds.midY)
    guard let hitTestResult = sceneView.hitTest(screenCentre, types: [.featurePoint]).first else { return }

    // Place an anchor for a virtual character.
    let anchor = ARAnchor(name: identifierString, transform: hitTestResult.worldTransform)
    sceneView.session.add(anchor: anchor)
    // add to item model
    ItemModel.shared.anchors.append((identifierString, anchor)

}

func showDirection(of object: ARAnchor) { // object: saved anchor

    if !Guide.shared.isExist {
        let startPoint = SCNVector3(0, 0 , -1)
        let targetPoint = SCNVector3(object.transform.columns.3.x, object.transform.columns.3.y, object.transform.columns.3.z)

        let guideNode = Guide.shared.setPosition(from: startPoint, to: targetPoint)

        // add the ship in the center of the view
        sceneView.pointOfView?.addChildNode(guideNode)

    }
}
/// Guide Class   
func setPosition(from start: SCNVector3, to target: SCNVector3) -> SCNNode {
    isExist = true
    guideNode.position = start
    targetPosition = target

    // create target node from saved anchor
    let desNode = SCNNode()
    desNode.position = targetPosition

    let lookAtConstraints = SCNLookAtConstraint(target: desNode)
    guideNode.constraints = [lookAtConstraints]

    return guideNode
}

// MARK: - ARSCNViewDelegate
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
    if let name = anchor.name, name.hasPrefix(identifierString) {
        // Create 3D Text
        let textNode: SCNNode = createNewBubbleParentNode(identifierString)
        node.addChildNode(textNode)
    }

}

我尝试了 SCNLookAtConstraint,但它并没有按预期工作,有什么建议吗?

演示


我注意到只有当手机的方向是横向时,它才能正常工作。 - Willjay
就我看来,除了背部朝向“香蕉”以外,一切都还好。原因是在SCNLookAtConstraint中,背面(-z轴)将充当指针。 - Alok Subedi
3个回答

3

我不知道这个对你的问题是否有帮助。如果没有,我会删除我的回答。

所以我遇到了同样的问题。我正在两个SCNVector对象之间绘制墙壁。问题是墙是双面的,因此在某些情况下,相机可见的部分会翻转。

例如,如果你从两个向量中绘制墙壁,然后添加另一个节点(平面节点),并使用相同的欧拉角添加节点(平面),则垂直翻转。

我已经搜索了许多解决方案并尝试了许多东西,但这对我有效。

class func node(from:SCNVector3,
                to:SCNVector3,height:Float,needToAlignToCamera:Bool) -> SCNNode {
    let distance = MathHelper().getMeasurementBetween(vector1: from, and: to)

    let wall = SCNPlane(width: CGFloat(distance),
                        height: CGFloat(height))
    wall.firstMaterial = wallMaterial()
    let node = SCNNode(geometry: wall)

    // always render before the beachballs
    node.renderingOrder = -10


    // HERE IS MAGIC LINES 
    //============================================

    let normalizedTO = to.normalized()
    let normalizedFrom = from.normalized()
    let angleBetweenTwoVectors = normalizedTO.cross(normalizedFrom)

    var from = from
    var to = to

    if angleBetweenTwoVectors.y > 0 && needToAlignToCamera {
        let temp = from
        from = to
        to = temp
    }
    //============================================


    node.position = SCNVector3(from.x + (to.x - from.x) * 0.5,
                               from.y + height * 0.5,
                               from.z + (to.z - from.z) * 0.5)

    // orientation of the wall is fairly simple. we only need to orient it around the y axis,
    // and the angle is calculated with 2d math.. now this calculation does not factor in the position of the
    // camera, and so if you move the cursor right relative to the starting position the
    // wall will be oriented away from the camera (in this app the wall material is set as double sided so you will not notice)
    // - obviously if you want to render something on the walls, this issue will need to be resolved.


    node.eulerAngles = SCNVector3(0, -atan2(to.x - node.position.x, from.z - node.position.z) - Float.pi * 0.5, 0)


    return node
}

扩展

func cross(_ vec: SCNVector3) -> SCNVector3 {
        return SCNVector3(self.y * vec.z - self.z * vec.y, self.z * vec.x - self.x * vec.z, self.x * vec.y - self.y * vec.x)
    }

func normalized() -> SCNVector3 {
        if self.length() == 0 {
            return self
        }

        return self / self.length()
    }

2

ARKit会通过委托方法renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode?更新锚点和相应的节点位置。

因此,您的约束想法是正确的,但是您需要通过委托方法提供节点,而不是直接将它们添加到场景中。

就像这样 -

    func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
        switch anchor {
        case MyAnchor:
          let constraint = // ...
          let node = // ...
          node.constraints = [constraint]
          return node
        default:
          return nil
        }
    }

你能详细说明一下吗?我已经更新了我的代码,但它仍然无法工作。 - Willjay
我认为showDirectionsetPosition方法都是错误的。你需要在委托方法中提供guideNode并在同一方法中设置约束。 - Maxim Volgin
我刚刚使用sceneView.pointOfView.addChildNodeship固定在屏幕中心,并且我希望ship(节点)指向anchor(另一个节点)。委托方法并不总是被调用,那么我该如何设置约束条件? - Willjay
这就是问题所在。为每个对象添加子类化的锚点会话中,例如GuideNodeAnchor。不要直接向场景中添加任何节点,只能通过委托方法提供它们。如果您在该委托方法中设置约束条件,ARKit将确保始终执行。 - Maxim Volgin

1
我注意到SCNLookAtConstraint只在我的手机方向与地平线平行时(屏幕朝上)起作用。
这行代码做了魔法!!guideNode.pivot = SCNMatrix4Rotate(guideNode.pivot, Float.pi, 0, 1, 1) 如果您感兴趣,请看这里
 public func showDirection(of object: arItemList) {

    guard let pointOfView = sceneView.pointOfView else { return }

    // remove previous instruction
    for node in pointOfView.childNodes {
        node.removeFromParentNode()
    }

    // target
    let desNode = SCNNode()
    let targetPoint = SCNVector3.positionFromTransform(object.1.transform)
    desNode.worldPosition = targetPoint

    // guide
    let startPoint = SCNVector3(0, 0 , -1.0)
    let guideNode = loadObject()
    guideNode.scale = SCNVector3(0.7, 0.7, 0.7)
    guideNode.position = startPoint

    let lookAtConstraints = SCNLookAtConstraint(target: desNode)
    lookAtConstraints.isGimbalLockEnabled = true
    // Here's the magic
    guideNode.pivot = SCNMatrix4Rotate(guideNode.pivot, Float.pi, 0, 1, 1)

    guideNode.constraints = [lookAtConstraints]
    pointOfView.addChildNode(guideNode)
}

嘿,你看到这个了吗?https://developer.apple.com/documentation/scenekit/scnnode/2867394-lookat?language=objc - Prashant Tukadiya

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接