我有一个 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
}
}
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() }
}
}