Highlight
RealityKit 在 visionOS 上新增了 Attachments、VideoPlayerComponent、Portals、Particle Emitters 和 Anchors 五大特性,让开发者可以把 SwiftUI 视图嵌入 3D 场景、在空间中播放视频、创建通往异世界的传送门、添加粒子特效,以及把内容锚定到真实世界的墙面和地板。
核心内容
Attachments:把 SwiftUI 视图放进 3D 场景
以前想在 3D 模型旁边加文字标签,需要创建纹理贴图或复杂的 3D 文本。Attachments 让这个过程变得像写 SwiftUI 一样简单(02:08)。
在 RealityView 的 attachments view builder 中定义 SwiftUI 视图,在 make closure 中通过 entity(for:) 获取对应的实体,然后像普通实体一样添加到场景中。文字标签、说明卡片、交互按钮都可以这样做。
VideoPlayerComponent:在 3D 空间中播放视频
VideoPlayerComponent 是 RealityKit 的新组件,用于在 3D 场景中嵌入视频(06:27)。
它自动根据视频宽高比生成矩形 mesh,支持所有 AVFoundation 格式,包括 2D 视频和 3D MV-HEVC 视频。视频默认高度 1 米,通过缩放实体调整大小。还支持透视着色(passthrough tinting),让环境光线匹配视频色调。
Portals:通往另一个世界的窗户
Portal 在空间中打开一扇”窗户”,透过它可以看到另一个独立的世界(11:15)。
这个世界有自己的光照、内容和空间关系。Portal 用 PortalMaterial 作为表面材质,配合 PortalComponent 链接到目标世界。适合创建魔法窗户、门廊、展示空间等效果。
Particle Emitters:粒子特效
ParticleEmitterComponent 可以在场景中创建火花、雪花、冲击波等视觉效果(15:09)。
可以在 Reality Composer Pro 中设计粒子效果,然后在运行时通过 RealityKit 动态调整属性。配合自定义 System,可以让粒子效果响应场景变化。
Anchors:把内容固定在现实世界
AnchorEntity 可以把 3D 内容锚定到墙面、地板或相对于头部/手部的位置(17:14)。
支持两种追踪模式:.continuous 持续跟随锚点移动,.once 定位后不再移动。锚定状态变化时可以通过 AnchoredStateChanged 事件监听。
详细内容
Attachments 代码示例
(02:30)
import SwiftUI
import RealityKit
struct MoonOrbit: View {
var body: some View {
RealityView { content, attachments in
guard let earth = try? await Entity(named: "Earth") else {
return
}
content.add(earth)
if let earthAttachment = attachments.entity(for: "earth_label") {
earthAttachment.position = [0, -0.15, 0]
earth.addChild(earthAttachment)
}
} attachments: {
Attachment(id: "earth_label") {
Text("Earth")
}
}
}
}
关键点:
RealityView的 make closure 新增attachments参数attachmentsview builder 中定义要嵌入的 SwiftUI 视图- 每个 Attachment 需要唯一的
id,可以是任意 Hashable 值 attachments.entity(for:)通过 id 获取对应的实体- 获取到的实体可以设置位置、作为子实体添加到其他实体上
- 可以用
glassBackgroundEffect()为 Attachment 添加毛玻璃背景
VideoPlayerComponent 代码示例
(08:03)
public func makeVideoEntity() -> Entity {
let entity = Entity()
let asset = AVURLAsset(url: Bundle.main.url(forResource: "tides_video",
withExtension: "mp4")!)
let playerItem = AVPlayerItem(asset: asset)
let player = AVPlayer()
entity.components[VideoPlayerComponent.self] = .init(avPlayer: player)
entity.scale *= 0.4
player.replaceCurrentItem(with: playerItem)
player.play()
return entity
}
关键点:
VideoPlayerComponent需要关联一个AVPlayer实例- 组件自动根据视频宽高比生成矩形 mesh
- 默认高度 1 米,这里缩放到 0.4 米(40 厘米)
- 支持所有 AVFoundation 格式,包括 MV-HEVC 3D 视频
- 自动显示 AVPlayer 提供的字幕
透视着色
(10:05)
var videoPlayerComponent = VideoPlayerComponent(avPlayer: player)
videoPlayerComponent.isPassthroughTintingEnabled = true
entity.components[VideoPlayerComponent.self] = videoPlayerComponent
关键点:
isPassthroughTintingEnabled让透视内容匹配视频色调- 类似 TV app 中看电影时的环境光效果
- 增强沉浸感,让虚拟内容和真实环境更协调
Portal 代码示例
(13:12)
struct PortalView : View {
var body: some View {
RealityView { content in
let world = makeWorld()
let portal = makePortal(world: world)
content.add(world)
content.add(portal)
}
}
}
public func makeWorld() -> Entity {
let world = Entity()
world.components[WorldComponent.self] = .init()
let environment = try! EnvironmentResource.load(named: "SolarSystem")
world.components[ImageBasedLightComponent.self] = .init(source: .single(environment),
intensityExponent: 6)
world.components[ImageBasedLightReceiverComponent.self] = .init(imageBasedLight: world)
let earth = try! Entity.load(named: "Earth")
let moon = try! Entity.load(named: "Moon")
let sky = try! Entity.load(named: "OuterSpace")
world.addChild(earth)
world.addChild(moon)
world.addChild(sky)
return world
}
public func makePortal(world: Entity) -> Entity {
let portal = Entity()
portal.components[ModelComponent.self] = .init(
mesh: .generatePlane(width: 1, height: 1, cornerRadius: 0.5),
materials: [PortalMaterial()]
)
portal.components[PortalComponent.self] = .init(target: world)
return portal
}
关键点:
WorldComponent标记实体树属于另一个独立世界- 世界中的内容只通过 Portal 表面可见
ImageBasedLightComponent为世界设置基于图像的光照PortalMaterial()让 mesh 成为 Portal 表面PortalComponent(target: world)把 Portal 链接到目标世界- 世界和 Portal 都要添加到 RealityView 的 content 中
粒子效果 System
(15:50)
public class ParticleTransitionSystem: System {
private static let query = EntityQuery(where: .has(ParticleEmitterComponent.self))
public func update(context: SceneUpdateContext) {
let entities = context.scene.performQuery(Self.query)
for entity in entities {
updateParticles(entity: entity)
}
}
}
public func updateParticles(entity: Entity) {
guard var particle = entity.components[ParticleEmitterComponent.self] else {
return
}
let scale = max(entity.scale(relativeTo: nil).x, 0.3)
let vortexStrength: Float = 2.0
let lifeSpan: Float = 1.0
particle.mainEmitter.vortexStrength = scale * vortexStrength
particle.mainEmitter.lifeSpan = Double(scale * lifeSpan)
entity.components[ParticleEmitterComponent.self] = particle
}
关键点:
- System 查询所有带有
ParticleEmitterComponent的实体 - 根据实体缩放动态调整粒子属性
vortexStrength控制粒子旋转强度lifeSpan控制粒子存活时间- 修改后必须把 Component 重新赋值回实体
- 粒子资源可以在 Reality Composer Pro 中设计
锚点到墙面
(18:19)
import SwiftUI
import RealityKit
struct PortalApp: App {
@State private var immersionStyle: ImmersionStyle = .mixed
var body: some SwiftUI.Scene {
ImmersiveSpace {
RealityView { content in
let anchor = AnchorEntity(.plane(.vertical, classification: .wall,
minimumBounds: [1, 1]))
content.add(anchor)
anchor.addChild(makePortal())
}
}
.immersionStyle(selection: $immersionStyle, in: .mixed)
}
}
关键点:
- 锚点需要在
ImmersiveSpace中使用 .plane(.vertical, classification: .wall)寻找垂直墙面minimumBounds: [1, 1]要求墙面至少 1x1 米- 找到匹配的锚点后,RealityKit 自动把内容附加到墙面
.mixedimmersion style 保留透视内容,同时显示虚拟内容- 锚点变换对 App 不可见,保护用户隐私
核心启发
-
做什么:做一个魔法窗户应用,透过墙上的 Portal 看到世界各地的风景。
-
为什么值得做:Portal 让用户在真实房间里打开通往任意世界的窗户,配合空间音频营造身临其境的感觉。
-
怎么开始:创建
WorldComponent定义世界内容,用AnchorEntity(.plane(.vertical))把 Portal 锚定到墙面。 -
做什么:开发一个教育应用,在 3D 模型旁边用 Attachment 显示知识点标签。
-
为什么值得做:Attachments 让 SwiftUI 视图和 3D 内容无缝结合,文字说明可以随模型一起移动。
-
怎么开始:在 RealityView 的
attachments中定义 Text 视图,用entity(for:)获取后作为子实体添加到模型上。 -
做什么:做一个沉浸式影院应用,在空间中播放 3D 电影。
-
为什么值得做:VideoPlayerComponent 支持 MV-HEVC 3D 视频,passthrough tinting 让环境光匹配电影色调。
-
怎么开始:用
VideoPlayerComponent(avPlayer: player)创建视频实体,设置isPassthroughTintingEnabled = true。 -
做什么:开发一个节日装饰应用,让用户在房间里放置粒子特效(雪花、烟花)。
-
为什么值得做:ParticleEmitterComponent 配合锚点可以把特效固定在真实空间位置。
-
怎么开始:在 Reality Composer Pro 中设计粒子效果,用
AnchorEntity锚定到天花板或墙面,通过 System 动态调整粒子属性。
关联 Session
- Build spatial experiences with RealityKit — RealityKit 基础:Entity、Component、System 与 RealityView
- Meet Reality Composer Pro — Reality Composer Pro 工具入门
- Explore materials in Reality Composer Pro — Reality Composer Pro 材质编辑
- Go beyond the window with SwiftUI — SwiftUI 沉浸空间与场景类型
评论
GitHub Issues · utterances