Code Repo: github.com/xuchi16/vis...
Project Path: github.com/xuchi16/vis...
基本概念
在VisionOS中,可以通过ARKit获取多种感知能力
-
平面检测(Plane detection) :检测周围环境中的水平表面(如桌子和地板)以及垂直平面(如墙壁和门),并使用它们来锚定内容。
-
世界跟踪(World tracking) :确定设备相对于其周围环境的位置和方向,并添加世界锚点以放置内容。
-
手部跟踪(Hand tracking) :使用人的手和手指位置作为自定义手势和交互的输入。
-
场景重建(Scene reconstruction) :构建人周围物理环境的网格,并将其融入沉浸式空间以支持交互。
-
图像跟踪(Image tracking) :在人的周围环境中寻找已知图像,并使用它们作为自定义内容的锚点。
目前,在visionOS模拟器中,仅支持了World tracking,其他均暂未支持。
通过这一感知能力,可以实现面向镜头、跟随镜头等功能,本文展示了让对象总是面向镜头的实现方式。
基本实现
- 定义
ARKitSessionManager
,主要负责初始化ARKitSession
、WorldTrackingProvider
以及获取设备实时姿态。
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
}
}
- 为了让目标物体能够有始终面向摄像机的特性,根据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)
}
}
}
- 打开全沉浸空间并加载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()
}
}
}