如何在RealityKit中实现广告牌效果(LookAt相机)?

4

我想在RealityKit中实现广告牌效果(平面始终朝向相机),我使用了Entity.Look()方法,但结果很奇怪,我甚至不能看到平面,我使用的脚本如下,那么问题出在哪里?

struct ARViewContainer: UIViewRepresentable {
    
    func makeUIView(context: Context) -> ARView {
        
        let arView = ARView(frame: .zero)
        let config = ARWorldTrackingConfiguration()
        config.planeDetection = .horizontal
        arView.session.run(config, options:[ ])
        arView.session.delegate = arView
        arView.createPlane()
        return arView        
    }
    
    func updateUIView(_ uiView: ARView, context: Context) { }
}

var planeMesh = MeshResource.generatePlane(width: 0.15, height: 0.15)
var planeMaterial = SimpleMaterial(color:.white,isMetallic: false)
var planeEntity = ModelEntity(mesh:planeMesh,materials:[planeMaterial])
var arCameraPostion : SIMD3<Float>!
var isPlaced = false

extension ARView : ARSessionDelegate{
    func createPlane(){
        let planeAnchor = AnchorEntity(plane:.horizontal)
        planeAnchor.addChild(planeEntity)
        self.scene.addAnchor(planeAnchor)
        //planeAnchor.transform.rotation = simd_quatf(angle: .pi, axis: [0,1,0])

    }
  
    public func session(_ session: ARSession, didUpdate frame: ARFrame){
        guard let arCamera = session.currentFrame?.camera else { return }
        if isPlaced {
            arCameraPostion = SIMD3(arCamera.transform.columns.3.x,0,arCamera.transform.columns.3.z)
            planeEntity.look(at: arCameraPostion, from: planeEntity.position, upVector: [0, 1, 0],relativeTo: nil)
        }
    }
   
    public func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
        isPlaced = true
    }
}


1
你找到解决方案了吗? - indrajit
尝试一下我的解决方案,@indrajit。 - Andy Jazz
2个回答

1

session(_:didUpdate:) 方法

尝试使用以下逻辑为 RealityKit 相机实现“广告牌”行为。您可以将此代码用作起点。它基于相机位置生成模型沿其本地 Y 轴的旋转。

import RealityKit
import ARKit

class ViewController: UIViewController {

    @IBOutlet var arView: ARView!
    var model = Entity()

    override func viewDidLoad() {
        super.viewDidLoad()

        arView.session.delegate = self
        
        let config = ARWorldTrackingConfiguration()
        arView.session.run(config)

        self.model = try! ModelEntity.load(named: "drummer")
        let anchor = AnchorEntity(world: [0, 0, 0])
        anchor.addChild(self.model)
        arView.scene.anchors.append(anchor)
    }
}

模型的枢轴点必须位于其中心(而不是距离模型一定距离处)。

extension ViewController: ARSessionDelegate {

    func session(_ session: ARSession, didUpdate frame: ARFrame) {

        let camTransform: float4x4 = arView.cameraTransform.matrix

        let alongXZPlane: simd_float4 = camTransform.columns.3

        let yaw: Float = atan2(alongXZPlane.x - model.position.x,
                               alongXZPlane.z - model.position.z)    
        print(yaw)

        // Identity matrix 4x4
        var positionAndScale = float4x4()
        
        // position
        positionAndScale.columns.3.z = -0.25
        
        // scale
        positionAndScale.columns.0.x = 0.01
        positionAndScale.columns.1.y = 0.01
        positionAndScale.columns.2.z = 0.01
        
        // orientation matrix
        let orientation = Transform(pitch: 0, yaw: yaw, roll: 0).matrix

        // matrices multiplication
        let transform = simd_mul(positionAndScale, orientation)   
        
        self.model.transform.matrix = transform
    }
}

subscribe(to:on:_:) 方法

另外,您可以实现对事件流的订阅。

import RealityKit
import Combine

class ViewController: UIViewController {

    @IBOutlet var arView: ARView!
    var model = Entity()
    var subs: [AnyCancellable] = []

    override func viewDidLoad() {
        super.viewDidLoad()    
        self.model = try! ModelEntity.load(named: "drummer")
        let anchor = AnchorEntity(world: [0, 0, 0])
        anchor.addChild(self.model)
        arView.scene.anchors.append(anchor)
        
        arView.scene.subscribe(to: SceneEvents.Update.self) { _ in
            let camTransform: float4x4 = self.arView.cameraTransform.matrix
            let alongXZPlane: simd_float4 = camTransform.columns.3    
            let yaw: Float = atan2(alongXZPlane.x - self.model.position.x,
                                   alongXZPlane.z - self.model.position.z)
    
            var positionAndScale = float4x4()    
            positionAndScale.columns.3.z = -0.25    
            positionAndScale.columns.0.x = 0.01
            positionAndScale.columns.1.y = 0.01
            positionAndScale.columns.2.z = 0.01    
            let orientation = Transform(pitch: 0, yaw: yaw, roll: 0).matrix
            let transform = simd_mul(positionAndScale, orientation)    
            self.model.transform.matrix = transform               
        }.store(in: &subs)
    }
}

1
ARSessionDelegate 可能不是 RealityKit 最好的选择...你最好订阅场景更新。 - maxxfrazer

0
在visionOS中,你可以尝试苹果的方法并应用BillboardComponent
下面的代码来自Swift Splash

BillboardComponent.swift

import Foundation
import RealityKit

/// The component that marks an entity as a billboard object which will always face the camera.
public struct BillboardComponent: Component, Codable {
    public init() {
    }
}

另外一个文件是 BillboardSystem
import ARKit
import Foundation
import OSLog
import RealityKit
import simd
import SwiftUI

/// An ECS system that points all entities containing a billboard component at the camera.
public struct BillboardSystem: System {
    
    static let query = EntityQuery(where: .has(SwiftSplashTrackPieces.BillboardComponent.self))
    
    private let arkitSession = ARKitSession()
    private let worldTrackingProvider = WorldTrackingProvider()
    
    public init(scene: RealityKit.Scene) {
        setUpSession()
    }
    
    func setUpSession() {
        
        Task {
            do {
                try await arkitSession.run([worldTrackingProvider])
            } catch {
                os_log(.info, "Error: \(error)")
            }
        }
    }
    
    public func update(context: SceneUpdateContext) {
        
        let entities = context.scene.performQuery(Self.query).map({ $0 })
        
        guard !entities.isEmpty,
              let pose = worldTrackingProvider.queryDeviceAnchor(atTimestamp: CACurrentMediaTime()) else { return }
        
        let cameraTransform = Transform(matrix: pose.originFromAnchorTransform)
        
        for entity in entities {
            entity.look(at: cameraTransform.translation,
                        from: entity.scenePosition,
                        relativeTo: nil,
                        forward: .positiveZ)
        }
    }
}

虽然,记住你必须在RealityKitComposer Pro中使用它,并在应用程序初始化器中注册它。

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