我怎样才能让一个实体不断地看着 RealityKit 中的相机,类似于 SceneKit 的 Billboard Constraint

How can I get an entity to constantly look at the camera in RealityKit similar to Billboard Constraint from SceneKit

我有一个应用程序正在尝试从 SceneKit 更新到 RealityKit,我很难在 RealityKit 中复制的功能之一是让一个实体不断地看着相机。在 SceneKit 中,这是通过向节点添加以下广告牌约束来实现的:

let billboardConstraint = SCNBillboardConstraint()

billboardConstraint.freeAxes = [.X, .Y]
startLabelNode.constraints = [billboardConstraint]

这将允许 startLabelNode 自由旋转,以便在 startLabelNode 不改变其位置的情况下始终面向相机。

但是,我似乎无法找到使用 RealityKit 执行此操作的方法。我已经尝试过“lookat”方法,它似乎并没有提供持续面对相机的能力。这是一个简短的示例应用程序,我尝试在 RealityKit 中实现这个版本,但它不提供让实体像在 SceneKit 中那样不断面对相机的能力:

import UIKit
import RealityKit
import ARKit

class ViewController: UIViewController, ARSessionDelegate {
    @IBOutlet weak var arView: ARView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        arView.session.delegate = self
        arView.environment.sceneUnderstanding.options = []
        arView.debugOptions.insert(.showSceneUnderstanding) // Display a debug visualization of the mesh.
        arView.renderOptions = [.disablePersonOcclusion, .disableDepthOfField, .disableMotionBlur] // For performance, disable render options that are not required for this app.
        arView.automaticallyConfigureSession = false
        let configuration = ARWorldTrackingConfiguration()
        if ARWorldTrackingConfiguration.supportsSceneReconstruction(.mesh) {
            configuration.sceneReconstruction = .mesh
        } else {
            print("Mesh Classification not available on this device")
            configuration.worldAlignment = .gravity
            configuration.planeDetection = [.horizontal, .vertical]
        }
        configuration.environmentTexturing = .automatic
        arView.session.run(configuration)
        
        UIApplication.shared.isIdleTimerDisabled = true // Prevent the screen from being dimmed to avoid interrupting the AR experience.
    }

    @IBAction func buttonPressed(_ sender: Any) {
        let screenWidth = arView.bounds.width
        let screenHeight = arView.bounds.height
        let centerOfScreen = CGPoint(x: (screenWidth / 2), y: (screenHeight / 2))
        
        if let raycastResult = arView.raycast(from: centerOfScreen, allowing: .estimatedPlane, alignment: .any).first
        {
            addStartLabel(at: raycastResult.worldTransform)
        }
    }
    
    func addStartLabel(at result: simd_float4x4) {
        let resultAnchor = AnchorEntity(world: result)
        resultAnchor.addChild(clickToStartLabel())
        arView.scene.addAnchor(resultAnchor)
    }
    
    func clickToStartLabel() -> ModelEntity {
        let text = "Click to Start Here"
        let textMesh = MeshResource.generateText(text, extrusionDepth: 0.001, font: UIFont.boldSystemFont(ofSize: 0.01))
        let textMaterial = UnlitMaterial(color: .black)
        let textModelEntity = ModelEntity(mesh: textMesh, materials: [textMaterial])
        textModelEntity.generateCollisionShapes(recursive: true)
        textModelEntity.position.x -= textMesh.width / 2
        textModelEntity.position.y -= textMesh.height / 2
        
        let planeMesh = MeshResource.generatePlane(width: (textMesh.width + 0.01), height: (textMesh.height + 0.01))
        let planeMaterial = UnlitMaterial(color: .white)
        let planeModelEntity = ModelEntity(mesh: planeMesh, materials: [planeMaterial])
        planeModelEntity.generateCollisionShapes(recursive:true)
        // move the plane up to make it sit on the anchor instead of in the middle of the anchor
        planeModelEntity.position.y += planeMesh.height / 2
        planeModelEntity.addChild(textModelEntity)
        
        // This does not always keep the planeModelEntity facing the camera
        planeModelEntity.look(at: arView.cameraTransform.translation, from: planeModelEntity.position, relativeTo: nil)
                
        return planeModelEntity
    }
}

extension MeshResource {
    var width: Float
    {
        return (bounds.max.x - bounds.min.x)
    }

    var height: Float
    {
        return (bounds.max.y - bounds.min.y)
    }
}

lookat 函数是让缺失的功能在 RealityKit 中工作的最佳方式,还是有更好的方式让实体不断面对相机?

k - 我对 RK 的了解不多,但假设实体是一个 scenekit 节点? - 然后对其设置约束,它会被迫一直面对 'targetNode'。提供它以您想要的方式工作,那么您可能必须试验节点最初是如何创建的,即它面向的方向。

func setTarget()
    {
        node.constraints = []
        let vConstraint = SCNLookAtConstraint(target: targetNode)
        vConstraint.isGimbalLockEnabled = true
        node.constraints = [vConstraint]
    }

我找到了问题的答案。添加以下代码块允许实体不断地看着相机:

func session(_ session: ARSession, didUpdate frame: ARFrame) {
    planeModelEntity.look(at: arView.cameraTransform.translation, from: planeModelEntity.position(relativeTo: nil), relativeTo: nil)
}