ARKit Поместите SCNNode лицом к камере

Я использую ARKit для отображения 3D-объектов. Мне удалось разместить узлы в реальном мире перед пользователем (он же камера). Но мне не удается заставить их смотреть в камеру, когда я их бросаю.

let tap_point=CGPoint(x: x, y: y)
let results=arscn_view.hitTest(tap_point, types: .estimatedHorizontalPlane)
guard results.count>0 else{
    return
}
guard let r=results.first else{
    return
}

let hit_tf=SCNMatrix4(r.worldTransform)
let new_pos=SCNVector3Make(hit_tf.m41, hit_tf.m42+Float(0.2), hit_tf.m43)

guard let scene=SCNScene(named: file_name) else{
    return
}
guard let node=scene.rootNode.childNode(withName: "Mesh", recursively: true) else{
    return
}
node.position=new_pos
arscn_view.scene.rootNode.addChildNode(node)

Узлы хорошо расположены на плоскости, перед камерой. Но все они смотрят в одном направлении. Я думаю, что я должен повернуть SCNNode но мне не удалось это сделать.

4 ответа

Сначала получим матрицу вращения камеры:

let rotate = simd_float4x4(SCNMatrix4MakeRotation(sceneView.session.currentFrame!.camera.eulerAngles.y, 0, 1, 0))

Затем объедините матрицы:

let rotateTransform = simd_mul(r.worldTransform, rotate)

Наконец, примените преобразование к вашему узлу, приведя к SCNMatrix4:

node.transform = SCNMatrix4(rotateTransform)

надеюсь, это поможет

РЕДАКТИРОВАТЬ

вот как вы можете создать SCNMatrix4 из simd_float4x4

   let rotateTransform = simd_mul(r.worldTransform, rotate)

  node.transform =  SCNMatrix4(m11: rotateTransform.columns.0.x, m12: rotateTransform.columns.0.y, m13: rotateTransform.columns.0.z, m14: rotateTransform.columns.0.w, m21: rotateTransform.columns.1.x, m22: rotateTransform.columns.1.y, m23: rotateTransform.columns.1.z, m24: rotateTransform.columns.1.w, m31: rotateTransform.columns.2.x, m32: rotateTransform.columns.2.y, m33: rotateTransform.columns.2.z, m34: rotateTransform.columns.2.w, m41: rotateTransform.columns.3.x, m42: rotateTransform.columns.3.y, m43: rotateTransform.columns.3.z, m44: rotateTransform.columns.3.w)
guard let frame = self.sceneView.session.currentFrame else {
    return
}
node.eulerAngles.y = frame.camera.eulerAngles.y

Вот мой код для SCNNode, стоящего перед камерой.. надеюсь, кто-нибудь

    let location = touches.first!.location(in: sceneView)

    var hitTestOptions = [SCNHitTestOption: Any]()
    hitTestOptions[SCNHitTestOption.boundingBoxOnly] = true

    let hitResultsFeaturePoints: [ARHitTestResult]  = sceneView.hitTest(location, types: .featurePoint)

    let hitTestResults = sceneView.hitTest(location)
    guard let node = hitTestResults.first?.node else {
        if let hit = hitResultsFeaturePoints.first {
            let rotate = simd_float4x4(SCNMatrix4MakeRotation(sceneView.session.currentFrame!.camera.eulerAngles.y, 0, 1, 0))
            let finalTransform = simd_mul(hit.worldTransform, rotate)
            sceneView.session.add(anchor: ARAnchor(transform: finalTransform))
        }
        return
    }

Вы хотите, чтобы узлы всегда были обращены к камере, даже когда камера движется? Вот для чего нужны ограничения SceneKit. Или SCNLookAtConstraint или же SCNBillboardConstraint может держать узел всегда указывая на камеру.

Хотите ли вы, чтобы узел находился лицом к камере, когда он был помещен, но затем неподвижен (чтобы вы могли перемещать камеру и видеть ее заднюю часть)? Есть несколько способов сделать это. Некоторые из них включают в себя забавную математику, но более простой способ справиться с этим может состоять в том, чтобы просто спроектировать ваши 3D-активы так, чтобы "фронт" всегда был в положительном направлении оси Z. Установите преобразование размещенного объекта на основе преобразования камеры, и его начальная ориентация будет соответствовать ориентации камеры.

Вот как я это сделал:

func faceCamera() {
    guard constraints?.isEmpty ?? true else {
        return
    }
    SCNTransaction.begin()
    SCNTransaction.animationDuration = 5
    SCNTransaction.completionBlock = { [weak self] in
        self?.constraints = []
    }
    constraints = [billboardConstraint]
    SCNTransaction.commit()
}

private lazy var billboardConstraint: SCNBillboardConstraint = {
    let constraint = SCNBillboardConstraint()
    constraint.freeAxes = [.Y]
    return constraint
}()

Как говорилось ранее SCNBillboardConstraint заставит узел всегда смотреть на камеру. Я оживляю его, чтобы узел не просто сразу встал на место, это необязательно. в SCNTransaction.completionBlock Я снимаю ограничение, также необязательно.

Также я установил SCNBillboardConstraint"s freeAxes, который настраивает, по какой оси узел следует за камерой, опять же необязательно.

Я хочу, чтобы узел располагался лицом к камере, когда я ее помещал, а затем оставляю здесь (и могу перемещаться). - Marie Dm Blockquote

Вы можете поместить объект лицом к камере, используя это:

if let rotate = sceneView.session.currentFrame?.camera.transform {
    node.simdTransform = rotate
}

Этот код избавит вас от блокировки карданного подвеса и других неприятностей.

Четырехкомпонентный вектор вращения задает направление оси вращения в первых трех компонентах и ​​угол поворота (в радианах) в четвертом. Вращением по умолчанию является нулевой вектор, указывающий отсутствие вращения. Вращение применяется относительно свойства узла simdPivot.

Свойства simdRotation, simdEulerAngles и simdOrientation влияют на аспект вращения свойства simdTransform узла. Любое изменение одного из этих свойств отражается в других.

https://developer.apple.com/documentation/scenekit/scnnode/2881845-simdrotation https://developer.apple.com/documentation/scenekit/scnnode/2881843-simdtransform

Другие вопросы по тегам