如何找到 3D 物体和 Vision Pro 设备相机的黑白角度(用户视图)

问题描述 投票:0回答:1

我有一个 3D 对象(广告牌),其中一侧包含广告,我想确定它是否对用户可见(通过视觉设备摄像头)。

这是我用来计算角度的代码,但似乎它没有按预期工作,因为我在相机和广告牌之间得到了错误的角度值。我使用点积公式来做到这一点。但不确定这是否是正确的方法。让我知道计算中有什么问题或替代方法。

class cityViewModel {
    var billboard = Entity()
}

struct CityView: View {
    private var cameraTracker = VisionProCameraTracker()
    var model = cityViewModel()
    
    var body: some View {
        RealityView { content in
            // Add the initial RealityKit content
            if let scene = try? await Entity(named: "newcity", in: citywithAdBundle) {
                content.add(scene)
                if let entity = scene.findEntity(named: "billboard") {
                    model.billboard = entity
                    print("Billboard position \(entity.position)")
                }
                updatingSceneEventsWith(content)
            }
        }
        .task {
            await cameraTracker.runArSession()
        }
    }
    
    private func updatingSceneEventsWith(_ content: RealityViewContent) {
        _ = content.subscribe(to: SceneEvents.Update.self) { _ in
            Task {
                let cameraTransform = await cameraTracker.getTransform()
                let x2 = cameraTransform!.columns.3.x
                let x1 = await model.billboard.position.x
                let y2 = cameraTransform!.columns.3.y
                let y1 = await model.billboard.position.y
                let z2 = cameraTransform!.columns.3.z
                let z1 = await model.billboard.position.z
                let distance = sqrtf( (x2-x1) * (x2-x1) +
                                      (y2-y1) * (y2-y1) +
                                      (z2-z1) * (z2-z1) )
                var formatted = String(format: "%.2f m", arguments: [distance])
                print("Distance from camera to billboard is:", formatted)
                
                let ab = (x1 * x2) + (y1 * y2) + (z1 * z2)
                let magnitudeOfAd = sqrtf( (x1 * x1) + (y1 * y1) + (y1 * y1) )
                let magnititeOfCamera = sqrtf( (x2 * x2) + (y2 * y2) + (y2 * y2) )
                let cosTheta = ab / (magnitudeOfAd * magnititeOfCamera)
                let angleInRadians = acos(cosTheta)
                let angleInDegrees = angleInRadians * 180 / .pi
                formatted = String(format: "%.2f m", arguments: [angleInDegrees])
                print("Angle b/w camera and billboard is \(formatted) degrees.")
            }
        }
    }
}

@Observable class VisionProCameraTracker {
    let session = ARKitSession()
    let worldTracking = WorldTrackingProvider()
    
    func runArSession() async {
        Task {
            try? await session.run([worldTracking])
        }
    }

    func getTransform() async -> simd_float4x4? {
        guard let deviceAnchor = worldTracking.queryDeviceAnchor(atTimestamp: 4)
        else { return nil }
        let transform = deviceAnchor.originFromAnchorTransform
        return transform
    }
}

swift 3d angle realitykit visionos
1个回答
0
投票

测量相机和模型之间的角度

SceneKit 中有 isNode(_:insideFrustumOf:) 实例方法,如果所寻找节点的边界框与由 POV 节点定义的透视相机的平截头体相交,可以帮助我们获得

true
值。类似的方法会对我们有很大帮助。不幸的是,VisionOS 1.2 的 RealityKit 没有这样一个有用的方法。所以我们只会满足于我们所拥有的。

为了以最简单的方式解决您的问题,我使用了

orientation(relativeTo:)
实例方法。此代码允许您找到沿 Y 轴和 X 轴任意方向的 0...45 度偏转角。按照问题中所述的相同方式设置 ARKit 的设备锚点跟踪(因为我们将使用
VisionProCameraTracker
类的对象)。然后创建一个锚点实体并将设备锚点的变换矩阵传递给它。现在您可以测量角度了。

这是我的代码:

import SwiftUI
import RealityKit
import ARKit

struct ContentView : View {
    let vpct = VisionProCameraTracker()
    let anchor = AnchorEntity(.head, trackingMode: .continuous)
    @State var billboard = Entity()
    
    var body: some View {
        RealityView { rvc in
            billboard = try! await Entity(named: "Billboard")
            rvc.add(billboard)
            rvc.add(anchor)
            
            let _ = rvc.subscribe(to: SceneEvents.Update.self) { _ in
                anchor.transform.matrix = vpct.getTransform()
                
                let radAngle = anchor.orientation(relativeTo: billboard).angle
                
                var radToDeg: Any = Int(radAngle * (180/Float.pi)) % 240
                
                if (radToDeg as! Int) > 45 {
                    radToDeg = "Angle is greater than 45 degrees"
                }
                if anchor.position.z < billboard.position.z {
                    radToDeg = "Billboard is not visible"
                }
                print(radToDeg, "degrees")
            }
        }
        .task { await vpct.runArSession() }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.