用 RealityKit 构建空间绘画 App
Build a spatial drawing app with RealityKit
2024年6月10日
一句话判断
这是 visionOS 2.0 开发者的必修课——SpatialTrackingSession、MeshResource extruding、ShaderGraph hover effect 这三个新 API 覆盖了空间应用开发中最常见的需求。
这场 Session 讲了什么
这场 Session 以构建一个空间绘画 App 为线索,系统介绍了 visionOS 2.0 中 RealityKit 的多项新能力。App 本身支持用户在空间中用手指捏合来绘画,提供实心笔刷和闪光笔刷两种类型,可以调整颜色和粗细。
内容分四个部分展开。首先是空间追踪:visionOS 2.0 引入了 SpatialTrackingSession API,替代了之前需要通过 ARKit 间接获取手部锚点数据的方式。其次是 UI 构建:用 SwiftUI Path 定义 2D 形状,再通过新的 MeshResource extruding API 将其挤出为 3D 模型。第三部分深入讲解了如何用 Metal 自定义生成笔刷几何体。最后介绍了动态纹理和 ShaderGraph 驱动的 hover 效果,用于打造启动画面。
整个 Session 技术密度很高,每个环节都有具体的 API 调用和设计决策,不是泛泛而谈的概念介绍。
值得深挖的点
SpatialTrackingSession:从 ARKit 到 RealityKit 的追踪简化
在 visionOS 1.0 中,获取手部追踪数据需要直接使用 ARKit 的 ARKitSession,配置 HandAnchorProvider,然后在帧回调中处理锚点更新。这个过程涉及较多异步处理和状态管理。visionOS 2.0 引入的 SpatialTrackingSession 将追踪能力直接内嵌到 RealityKit 的工作流中。
使用方式是声明你的 App 需要哪些追踪能力(目前支持 hand data),然后调用 run() 方法。系统会弹出权限请求弹窗,用户授权后,你的 AnchorEntity 的 transform 就会自动更新。这意味着你不再需要手动将 ARKit 的锚点数据映射到 RealityKit 实体上,整个数据流被大幅简化。还有一个重要细节:如果使用了 SpatialTrackingSession,AnchorEntity 还能与 RealityKit 的物理系统交互,这为手势驱动的物理交互打开了可能性。
MeshResource extruding 与 foveated rendering 的注意事项
MeshResource extruding 是今年新增的 2D 转 3D 的便捷 API。你只需要提供一个 SwiftUI Path、指定深度和分辨率,就能生成 3D 网格。Session 中用它来生成画布边界的环形网格,因为画布大小是动态可调的,所以网格需要实时重建。
但这里有一个容易踩的坑:visionOS 使用 foveated rendering,周边视觉区域以较低分辨率渲染。如果你的几何体包含细薄的高对比边缘,用户可能会看到闪烁的伪影。Session 中给出的解决方案是增加几何体厚度,避免出现过薄的高对比边缘。这个限制在文档中可能不会显眼,但在实际开发中会直接影响视觉质量。
代码片段
场景一:使用 SpatialTrackingSession 请求手部追踪权限
import RealityKit
// 创建锚点实体,分别追踪拇指尖和食指尖
let thumbAnchor = AnchorEntity(.hand(.left, location: .thumbTip))
let indexAnchor = AnchorEntity(.hand(.left, location: .indexFingerTip))
immersiveSpace.addChild(thumbAnchor)
immersiveSpace.addChild(indexAnchor)
// 使用新的 SpatialTrackingSession 请求追踪权限
let session = SpatialTrackingSession()
let configuration = SpatialTrackingSession.Configuration(
tracking: [.hand] // 声明需要手部追踪数据
)
// 调用 run() 会弹出系统权限弹窗
let unapproved = session.run(configuration)
// 检查是否有未授权的能力
if unapproved.isEmpty {
// 全部授权,AnchorEntity 的 transform 将自动更新
// 可以直接读取 thumbAnchor.transform 获取手指位置
} else {
// 用户拒绝了部分权限,需要降级处理
// 坑点:即使权限被拒绝,AnchorEntity 仍会更新视觉姿态
// 但 transform 属性不会更新,不要依赖视觉位置做逻辑判断
}
场景二:用 MeshResource extruding 从 2D 路径生成 3D 网格
import SwiftUI
import RealityKit
// 定义环形路径(外圈绿色,内圈红色)
let path = Path { path in
// 外圈
path.addArc(center: .zero, radius: outerRadius,
startAngle: .zero, endAngle: .degrees(360), clockwise: false)
// 内圈
path.addArc(center: .zero, radius: innerRadius,
startAngle: .zero, endAngle: .degrees(360), clockwise: true)
}
// 使用 even-odd 填充规则创建空心环形
let shape = MeshResource.ShapeExtrusion(
path: path,
depth: 0.02, // 挤出深度
resolution: .init(verticesPerArc: 64) // 曲线精度
)
// 生成 MeshResource
// 坑点:分辨率太低会导致圆形出现明显锯齿
// 但太高会影响实时重建的性能,需要找到平衡点
let meshResource = try await MeshResource(extruding: shape)
let entity = ModelEntity(mesh: SimpleMeshResource(mesh: meshResource))
场景三:使用新的 highlight hover 效果
import RealityKit
// visionOS 1.0 只有默认的 spotlight hover 效果
// visionOS 2.0 新增了 highlight 和 shader-backed 两种效果
// 方式一:高亮效果(Session 中用于画布控制柄)
let hoverEffect = HoverEffectComponent(.highlight(
color: .blue, // 高亮颜色
strength: 0.8 // 强度,值越大越鲜艳
))
handleEntity.components.set(hoverEffect)
// 方式二:ShaderGraph 驱动的 hover 效果(更灵活)
// 可以通过 ShaderGraph 材质精确控制 hover 时的外观变化
// 比如发光扩散、颜色渐变等自定义动画
// 坑点:ShaderGraph hover 效果需要在材质中正确配置 hover 输入端口
// 否则效果不会生效且不会有编译错误提示
最佳实践
- 权限请求时机:不要在 App 启动时就请求追踪权限。应该等用户真正要开始绘画时再触发,这样用户能理解为什么需要这个权限,授权率会更高。
- 细薄几何体规避:在设计 visionOS 的 3D 界面时,主动避免细薄的高对比边缘。这不是建议,而是硬性要求——foveated rendering 机制决定了细线在视觉上会出现闪烁。
- 几何体分辨率权衡:MeshResource extruding 的 resolution 参数需要根据场景调优。静态几何体可以用高分辨率,动态重建的几何体需要控制顶点数以维持帧率。
还有什么值得关注
- Session 提到笔刷几何体通过 Metal 直接生成,这对性能敏感的空间应用来说是关键技术路径
- ShaderGraph 现在可以直接用于 hover 效果,这意味着空间 UI 的交互反馈可以做得非常精致
- RealityKit 的物理系统现在可以和 SpatialTrackingSession 的锚点交互,手势驱动的物理效果成为可能