VisionPro开发 - 如何让物体总是面向镜头?


首页:漫游Apple Vision Pro

Code Repo: github.com/xuchi16/vis...

Project Path: github.com/xuchi16/vis...


基本概念

在VisionOS中,可以通过ARKit获取多种感知能力

  1. 平面检测(Plane detection) :检测周围环境中的水平表面(如桌子和地板)以及垂直平面(如墙壁和门),并使用它们来锚定内容。

  2. 世界跟踪(World tracking) :确定设备相对于其周围环境的位置和方向,并添加世界锚点以放置内容。

  3. 手部跟踪(Hand tracking) :使用人的手和手指位置作为自定义手势和交互的输入。

  4. 场景重建(Scene reconstruction) :构建人周围物理环境的网格,并将其融入沉浸式空间以支持交互。

  5. 图像跟踪(Image tracking) :在人的周围环境中寻找已知图像,并使用它们作为自定义内容的锚点。

目前,在visionOS模拟器中,仅支持了World tracking,其他均暂未支持

通过这一感知能力,可以实现面向镜头、跟随镜头等功能,本文展示了让对象总是面向镜头的实现方式。

基本实现

  1. 定义ARKitSessionManager,主要负责初始化ARKitSessionWorldTrackingProvider以及获取设备实时姿态。
swift 复制代码
import SwiftUI
import ARKit

@MainActor
class ARKitSessionManager: ObservableObject {

    let session = ARKitSession()
    let worldTracking = WorldTrackingProvider()

    func startSession() async {
        if WorldTrackingProvider.isSupported {
            do {
                try await session.run([worldTracking])
            } catch {
                assertionFailure("Failed to run session: (error)")
            }
        }
    }

    func getOriginFromDeviceTransform() -> simd_float4x4 {
        guard let pose = worldTracking.queryDeviceAnchor(atTimestamp: CACurrentMediaTime()) else {
            return simd_float4x4()
        }
        return pose.originFromAnchorTransform
    }
}
  1. 为了让目标物体能够有始终面向摄像机的特性,根据6. 物体移动 一节介绍的ECS模式,需要定义对应的Component和System。
  • Component记录实体状态数据
  • System控制实体行为

这里FaceComponent没有过多状态,只是为了让System获取摄像机信息,记录了对应的ARKitSessionManager供System使用。

swift 复制代码
import RealityKit
public struct FaceComponent: Component {
    let manager: ARKitSessionManager?
}

FollowSystem中,

  • 通过followEntity.manager!.getOriginFromDeviceTransform()获取设备Transform,并且提取其中位置坐标
  • 通过entity.transform.translation获取实体位置坐标

Entity上提供了一个非常易用的函数look(at:from:upVector:relativeTo:),用于定位和调整实体的朝向,使其从给定位置看向一个目标。我们可以在任何实体上使用这个方法,但它特别适用于定位摄像头和灯光,使其瞄准空间中的特定点。

swift 复制代码
import RealityKit
public struct FaceSystem: System {
    static let faceQuery = EntityQuery(where: .has(FaceComponent.self))
    
    public init(scene: Scene) {
    }
    
    public func update(context: SceneUpdateContext) {
        let entities = context.scene.performQuery(Self.faceQuery)
        
        for entity in entities {
            guard let followEntity = entity.components[FaceComponent.self] else {
                continue
            }
            
            if followEntity.manager == nil {
                return
            }
            let cameraTransform = followEntity.manager!.getOriginFromDeviceTransform()
            
            let cameraPosition = SIMD3<Float>(cameraTransform.columns.3.x, cameraTransform.columns.3.y, cameraTransform.columns.3.z)
            let entityPosition = entity.transform.translation
            
            entity.look(at: cameraPosition, from: entityPosition, relativeTo: nil)
        }
    }
}
  1. 打开全沉浸空间并加载3D对象,为了后续能够使用ARKit中的信息,我们需要初始化ARKitSessionManager并启动ARKitSession。有了ARKitSessionManager后,将其传递给新建的FaceComponent,最终附属到实体上,这样FaceSystem就能够获得设备和实体的位置信息,并实现追踪效果了。
swift 复制代码
struct ImmersiveView: View {
    @State var entity = Entity()
    @ObservedObject var arkitSessionManager = ARKitSessionManager()

    var body: some View {
        RealityView { content in
            let tv = try! await Entity(named: "tv_retro")
            tv.transform.rotation = simd_quatf(angle: .pi, axis: [0, 1, 0])
            entity.addChild(tv)

            let followComponent = FaceComponent(manager: arkitSessionManager)
            entity.components[FaceComponent.self] = followComponent
            entity.position = SIMD3<Float>(x: 0, y: 1.2, z: 0)
            content.add(entity)
        }
        .task {
            await arkitSessionManager.startSession()
        }
    }
}

最终效果

相关推荐
struggle202515 小时前
Ollmao (OH-luh-毛程序包及源码) 是一款原生 SwiftUI 应用程序,它与 Ollama 集成,可在 Mac 上本地运行强大的 AI 模型
ios·swiftui·swift
货拉拉技术1 个月前
货拉拉用户端SwiftUI踩坑之旅
ios·swiftui·swift
ZacJi1 个月前
巧用 allowsHitTesting 自定义 SignInWithAppleButton
ios·swiftui·swift
刘争Stanley1 个月前
SwiftUI 是如何改变 iOS 开发游戏规则的?
ios·swiftui·swift
1024小神1 个月前
在swiftui中使用Alamofire发送请求获取github仓库里的txt文件内容并解析
ios·github·swiftui
大熊猫侯佩1 个月前
SwiftUI 撸码常见错误 2 例漫谈
swiftui·xcode·tag·tabview·preview·coredata·fetchrequest
东坡肘子2 个月前
肘子的 Swift 周报 #063|异种肾脏移植取得突破
swiftui·swift·apple
恋猫de小郭2 个月前
什么?Flutter 可能会被 SwiftUI/ArkUI 化?全新的 Flutter Roadmap
flutter·ios·swiftui
靴子学长2 个月前
iOS + watchOS Tourism App(含源码可简单复现)
mysql·ios·swiftui
hxx2212 个月前
iOS swift开发系列--如何给swiftui内容视图添加背景图片显示
ios·swiftui·swift