web-dev-qa-db-fra.com

Placez un objet SceneKit devant l'orientation actuelle de SCNCamera

Je souhaite créer un nouveau nœud SceneKit lorsque l'utilisateur appuie sur l'écran et le fait apparaître directement devant la caméra à une distance définie. Pour les tests, ce sera un SCNText qui lit "vous avez tapé ici". Il doit également être perpendiculaire à la ligne de mire, c’est-à-dire «faire face» à la caméra.

Alors, étant donné le self.camera.orientation SCNVector4 (ou similaire), comment puis-je:

  1. produire le SCNVector3 qui place le nœud devant "la caméra" à une distance donnée?
  2. produire le SCNVector4 (ou SCNQuaternion) qui oriente le nœud pour qu'il "fasse face" à la caméra?

Je soupçonne que la réponse à (2) est que c'est la même chose que `self.camera.orientation? Mais (1) m'a un peu perdu.

Je vois un certain nombre de questions similaires, comme celle-ci , mais pas de réponse.

9
Maury Markowitz

(Swift 4)

Hé, vous pouvez utiliser cette fonction plus simple si vous voulez placer l’objet à une certaine position par rapport à un autre nœud (par exemple le nœud de la caméra) et également dans la même orientation que le nœud de référence:

func updatePositionAndOrientationOf(_ node: SCNNode, withPosition position: SCNVector3, relativeTo referenceNode: SCNNode) {
    let referenceNodeTransform = matrix_float4x4(referenceNode.transform)

    // Setup a translation matrix with the desired position
    var translationMatrix = matrix_identity_float4x4
    translationMatrix.columns.3.x = position.x
    translationMatrix.columns.3.y = position.y
    translationMatrix.columns.3.z = position.z

    // Combine the configured translation matrix with the referenceNode's transform to get the desired position AND orientation
    let updatedTransform = matrix_multiply(referenceNodeTransform, translationMatrix)
    node.transform = SCNMatrix4(updatedTransform)
}

Si vous souhaitez placer le "nœud" 2 m juste devant un certain "cameraNode", appelez-le ainsi:

let position = SCNVector3(x: 0, y: 0, z: -2)
updatePositionAndOrientationOf(node, withPosition: position, relativeTo: cameraNode)

Edit: Obtenir le nœud de la caméra

Pour obtenir le nœud de caméra, cela dépend si vous utilisez SCNKit, ARKit ou un autre framework. Vous trouverez ci-dessous des exemples pour ARKit et SceneKit.

Avec ARKit, vous avez ARSCNView pour restituer les objets 3D d’un SCNScene superposés au contenu de la caméra. Vous pouvez obtenir le noeud de la caméra à partir de la propriété pointOfView d’ARSCNView:

let cameraNode = sceneView.pointOfView

Pour SceneKit, vous avez une vue SCNView qui rend les objets 3D d’un SCNScene. Vous pouvez créer des noeuds de caméra et les positionner où vous voulez, de sorte que vous fassiez quelque chose comme:

let scnScene = SCNScene()
// (Configure scnScene here if necessary)
scnView.scene = scnScene
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3(0, 5, 10)  // For example
scnScene.rootNode.addChildNode(cameraNode)

Une fois qu'un nœud de caméra a été configuré, vous pouvez accéder à la caméra actuelle de la même manière que ARKit:

let cameraNode = scnView.pointOfView
10
Pablo

Et je l’ai compris en rassemblant plusieurs autres réponses ... commençons par ces méthodes:

func pointInFrontOfPoint(point: SCNVector3, direction: SCNVector3, distance: Float) -> SCNVector3 {
    var x = Float()
    var y = Float()
    var z = Float()

    x = point.x + distance * direction.x
    y = point.y + distance * direction.y
    z = point.z + distance * direction.z

    let result = SCNVector3Make(x, y, z)
    return result
}

func calculateCameraDirection(cameraNode: SCNNode) -> SCNVector3 {
    let x = -cameraNode.rotation.x
    let y = -cameraNode.rotation.y
    let z = -cameraNode.rotation.z
    let w = cameraNode.rotation.w
    let cameraRotationMatrix = GLKMatrix3Make(cos(w) + pow(x, 2) * (1 - cos(w)),
                                              x * y * (1 - cos(w)) - z * sin(w),
                                              x * z * (1 - cos(w)) + y*sin(w),

                                              y*x*(1-cos(w)) + z*sin(w),
                                              cos(w) + pow(y, 2) * (1 - cos(w)),
                                              y*z*(1-cos(w)) - x*sin(w),

                                              z*x*(1 - cos(w)) - y*sin(w),
                                              z*y*(1 - cos(w)) + x*sin(w),
                                              cos(w) + pow(z, 2) * ( 1 - cos(w)))

    let cameraDirection = GLKMatrix3MultiplyVector3(cameraRotationMatrix, GLKVector3Make(0.0, 0.0, -1.0))
    return SCNVector3FromGLKVector3(cameraDirection)
}

Maintenant, vous pouvez l'appeler comme ceci:

let rectnode = SCNNode(geometry: rectangle)
let dir = calculateCameraDirection(cameraNode: self.camera)
let pos = pointInFrontOfPoint(point: self.camera.position, direction:dir, distance: 18)
rectnode.position = pos
rectnode.orientation = self.camera.orientation
sceneView.scene?.rootNode.addChildNode(rectnode)

La distance 18 le positionne entre une grille sur une sphère de rayon 10 et une image d’arrière-plan à 25. Elle a l’air vraiment bonne et la performance sur mes 5 est étonnamment bonne.

12
Maury Markowitz

Rendre les choses simples:

let constraint = SCNTransformConstraint(inWorldSpace: true) { node, transform -> SCNMatrix4 in
    let distance: Float = 1.0

    let newNode = SCNNode()
    let worldFront = self.sceneView!.pointOfView!.simdWorldFront * distance

    newNode.transform = self.sceneView!.pointOfView!.transform
    newNode.transform = SCNMatrix4Translate(newNode.transform, worldFront.x, worldFront.y, worldFront.z)

    return newNode.transform
}
0
Maciej Stramski