Place the SceneKit object in front of the current SCNCamera orientation

I want to create a new SceneKit node when the user closes the screen and appears right in front of the camera at a given distance. For testing, this will be a SCNText reading that reads "you listened here." It should also be at right angles to the line of sight, that is, “facing” the camera.

So, given self.camera.orientationSCNVector4 (or similar), how can I:

  • creates a SCNVector3 that puts a node in the "front" of the camera at a given distance?
  • create SCNVector4 (or SCNQuaternion) that orientates the node so that it "looks" at the camera?

I suspect the answer to (2) is that it is the same as `self.camera.orientation? But (1) lost me a little.

I see a number of similar questions, for example this one , but there are no answers.

+8
source share
4 answers

(Swift 4)

Hey, you can use this simpler function if you want to place the object in a certain position relative to another node (for example, the camera node), as well as in the same orientation as the reference node:

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 transform to get the desired position AND orientation
    let updatedTransform = matrix_multiply(referenceNodeTransform, translationMatrix)
    node.transform = SCNMatrix4(updatedTransform)
}

If you want to put a 'node' in 2 meters right in front of a specific 'cameraNode', you would call it like this:

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

Change: Getting Camera Node

To get the camera node, it depends on whether you use SCNKit, ARKit, or another framework. The following are examples for ARKit and SceneKit.

ARKit ARSCNView SCNScene, . ARSCNView pointOfView:

let cameraNode = sceneView.pointOfView

SceneKit SCNView, SCNScene. , , - :

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)

, ARKit:

let cameraNode = scnView.pointOfView
+9

- , SceneKit , .

[] SCNVector3, "" ?

, , "" - -Z. - (0, 0, -10) , (0, 0, -10) 10 . :

  1. .

    cameraNode.addChildNode(textNode)
    textNode.position = SCNVector3(x: 0, y: 0, z: -10)
    

    , : , textNode . ( - - , .)

    -, , , , . ( SCNText SCNText .)

  2. ( iOS 11/macOS 10.13/Xcode 9/ ..) SceneKit . , SIMD / SCN, SIMD , . , :

    textNode.simdPosition = camera.simdWorldFront * distance
    

    (simdWorldFront 1,0, , .)

  3. - iOS 10/macOS 10.12/etc, . ( rootNode ), , rootNode . , :

    func sceneSpacePosition(inFrontOf node: SCNNode, atDistance distance: Float) -> SCNVector3 {
        let localPosition = SCNVector3(x: 0, y: 0, z: CGFloat(-distance))
        let scenePosition = node.convertPosition(localPosition, to: nil) 
             // to: nil is automatically scene space
        return scenePosition
    }
    textNode.position = sceneSpacePosition(inFrontOf: camera, atDistance: 3)
    

    , "" . , , . , .

[] SCNVector4 ( SCNQuaternion), , "" ?

SceneKit, , .

  1. (1) - - , , - . , , SCNText . SCNText ( , -Z, +Y +X ).

    - , , , . , SCNPyramid +Y, , , (, ), X .pi/2 ( eulerAngles, rotation, orientation pivot.)

    , , , , , , .

  2. (2) , , , , . API, . , API: SCNLookAtConstraint " " ( -Z ), SCNBillboardConstraint - , , - , .

    :

    textNode.constraints = [ SCNBillboardConstraint() ]
    

sceneSpacePosition sceneSpacePosition , transform, eulerAngles, rotation orientation , , , , , / - , .

SceneKit : "" "". - , transform ( ..). / . , , , :

textNode.position = sceneSpacePosition(inFrontOf: camera.presentation, atDistance: 3)
                                                        ~~~~~~~~~~~~~

( ? , : position , . position - , . position.)

+25

, ... :

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)
}

:

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)

A distance of 18 places it between the grid on a sphere at a radius of 10 and the background image at 25. It looks really good, and the performance on my 5s is surprisingly good.

+12
source

Making it simple:

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
source

Source: https://habr.com/ru/post/1668764/


All Articles