
-
RealityKit 的新功能
利用全新的 RealityKit 功能尽情挥洒创意,这些功能可帮助你为 iOS、iPadOS、macOS、Apple tvOS 和 visionOS 构建丰富的 3D 内容。了解如何直接通过 RealityKit 来访问 ARKit 数据。探究如何使用对象操作功能与 3D 内容进行更自然的交互。通过一个交互式示例,探索一些适用于场景理解、环境融合、实例化等方面的新 API。
章节
- 0:00 - 简介
- 3:19 - 锚定方面的更新
- 6:52 - ManipulationComponent
- 10:01 - 场景理解
- 11:18 - EnvironmentBlendingComponent
- 12:26 - MeshInstancesComponent
- 16:28 - 沉浸式媒体
- 21:43 - 配件及其他
资源
相关视频
WWDC25
-
搜索此视频…
大家好 我是 Laurence 是 RealityKit 团队的软件工程师 欢迎观看我的讲座 “RealityKit 的新功能” 在这次的讲座中 我将介绍 要在今年发布的一些 RealityKit 新功能
RealityKit 是我们 在 2019 年发布的 目的是让大家能够 将 3D 内容集成到 App 中 实现逼真的渲染效果 和增强版沉浸式体验 从那时起 我们收到了 很多开发者的反馈 帮助我们不断对这个框架进行优化 RealityKit 提供了丰富的功能 能将 3D 内容与 现实世界的环境无缝融合 让大家能够在 visionOS 上 打造沉浸式 App 和游戏 除了 visionOS RealityKit 的诸多关键功能 还支持 iOS、iPadOS 和 macOS 借助 RealityKit 的跨平台功能 你可以把为某个平台开发的 App 移植到其他平台上 只需对代码进行极少的改动 今年 我要带来的一个好消息是 RealityKit 现在已经支持 最新的 Apple tvOS! 现在 你可以将现有的 App 和体验移植到 AppleTV 也可以为这种大屏设备重新开发! 全系 AppleTV 4K 均支持 RealityKit 今年的 RealityKit 更新 带来了很多新功能 能让我们更轻松地 打造融合虚拟场景和现实世界 的 3D 体验 在这次讲座中 我将介绍其中的一些功能 比如 ManipulationComponent、 EnvironmentBlendingComponent、 MeshInstancesComponent 等 我将使用其中的一些功能 来构建一款空间益智游戏 游戏一开始 面前的桌子上 放着一个上锁的宝箱 周围有多个可以交互的对象 其中一个对象的下面 有能打开宝箱的钥匙 我们可以拿起这些对象 看看钥匙藏在哪个对象里 如果找到了钥匙 我们就可以打开宝箱并看到奖品:
一场小型烟花秀!
首先我将使用 RealityKit 中 新的原生 ARKit 支持 把模型锚定到玩家面前的空间 并处理锚点生命周期的变化 然后 我将展示如何使用新的 ManipulationComponent 添加场景中与 3D 实体的交互 我还将使用 PhysicsBodyComponent 让它们在释放时 能呈现逼真的下落效果 然后 我将使用新的 SceneUnderstanding API 让游戏实体 与我们的场景理解网格发生碰撞
接着我将使用 EnvironmentBlendingComponent 让 App 更好地与现实世界融为一体
接下来我将展示如何使用 新的 MeshInstancesComponent 高效地绘制可以用来装饰场景的 3D 模型的多个实例 然后 我将介绍一些 关于沉浸式媒体的更新 我还将介绍其他更新 例如空间配件、 实体更新等 首先 我将使用一个新的 API 将 3D 模型锚定到现实世界环境中 借助这个 API 可直接 通过 RealityKit 访问 ARKit 数据 我将使用 RealityKit AnchorEntity 将我的游戏放置在一张桌子上 锚点实体用于将虚拟内容 附加到现实世界的表面 今年我们通过直接 公开 ARKit 锚定数据 让 AnchorEntities 变得更加强大 我来展示一下具体的操作过程 要直接通过 RealityKit 访问 ARKit 数据 需要先 创建一个 SpatialTrackingSession 会话配置将告诉 RealityKit 在 AnchorEntities 的状态发生变化时 将新的 AnchorStateEvents 传递给你的 App 然后 你可以设置 AnchorEntity 来筛选你需要的锚点的属性 在我的 App 中 我需要 一张具有特定维度的桌子 一旦 RealityKit 找到与我在 AnchorEntity 中设置的属性匹配的锚点 它将触发 AnchorStateEvent AnchorStateEvent 实例包含 ARKit 数据 比如转换和锚点的范围 我们可以用它们来定位我的游戏 我们来看看怎么通过代码实现 首先我要创建一个 SpatialTrackingSession 它支持把 RealityKit AnchorEntities 跟踪到环境 在我的游戏中 我需要跟踪一个平面 用来在上面生成宝箱 我要设置启用了平面追踪的 SpatialTrackingSession 配置
现在 我可以通过运行刚刚 创建的配置来启动跟踪会话 接下来 我可以生成一个 AnchorEntity 用来将游戏放置在桌面上 我把 AnchorEntity 分类为桌子 桌子应该是一个水平表面 与之关联的最小边界 的边长为 15 厘米 这个 AnchorEntity 一开始 会处于非锚定状态 但当检测到与提供的分类和 边界匹配的桌子平面时 AnchorEntity 将变为锚定状态 为了在锚点状态发生变化时收到更新 我需要使用新的 AnchorStateEvents API
通过 AnchorStateEvents API 我们可以订阅事件 例如在实体锚定时、 即将取消锚定时、 或者锚定失败时 我将在 App 中使用 DidAnchor 事件来 将游戏实体定位在桌面的边界内
在我的代码中 我订阅了 DidAnchor 事件 以了解锚点实体何时 成功锚定到环境 事件结构为我 提供了已更新的锚点实体 其中包含新的 ARKitAnchorComponent 这个组件保存着 ARKit 数据 比如我可以用于 将游戏实体定位到锚定表面 的范围和转换 我可以使用 ARKitAnchorComponent 的 锚点属性访问这些数据
要使用锚点属性 必须将它 强制转换为相应的 ARKit 锚点类型 在本例中我将尝试 将它强制转换为 PlaneAnchor 因为我的 AnchorEntity 设置为查找平面
现在 我可以访问锚点的 原始 ARKit 范围和转换 我将使用 ARKit 转换 特别是 originFromAnchorTransform 和 anchorFromExtentTransform 来定位游戏 并确保它位于锚定表面的中心 现在 当我打开沉浸式空间时 游戏对象将在找到合适的表面后生成
接下来 我将使用 RealityKit 中的 新 ManipulationComponent 向场景添加交互 ManipulationComponent 简化了 拿起和旋转场景 中的 3D 实体的流程 它甚至还支持换手等高级手势!
我将使用 ManipulationComponent 让玩家能够拿起 并旋转游戏对象 以找到它们下方的钥匙 我将展示如何将这个功能 添加到游戏中 若想详细了解 ManipulationComponent 的工作机制 以及你能用它做什么 请观看我们的讲座 “搭配使用更出色: SwiftUI 和 RealityKit” 要支持拿起实体和与实体交互 只需要调用 ManipulationComponent configureEntity 函数 这个函数会自动将 必要的 InputTarget、Collision、 HoverEffect 和 Manipulation 组件添加到实体 就是这样! 现在 你可以用手拿起和旋转实体 但你会注意到 当你释放这些对象时 它们能以平滑的动画方式 回到原先的位置
我将返回我的代码并在要停留的 ManipulationComponent 上 设置“releaseBehavior”属性
然后我为我的实体指定 这个 manipulationComponent 实例 这样可以避免 在释放对象时 对象自动返回原位 它将保持不动 接下来 我需要让它掉在地上 为此 我将向实体添加 PhysicsBodyComponent 我希望谨慎一点 只在玩家没有拿住和拿起对象时 PhysicsBodyComponent 才会受重力作用 新的 ManipulationEvents API 能帮我们轻松实现这一点!
ManipulationEvents 是 RealityKit 发出的事件 可以描述在与玩家交互时 实体经历的状态
例如 玩家释放实体 将触发 WillRelease 事件 同样 还有其他事件 如 WillBegin、WillEnd、 DidUpdateTransform 以及 DidHandOff 如需了解更多详细信息 请阅读 developer.apple.com/cn 上的文档
我将使用 WillBegin 和 WillEnd 事件来确保我的游戏实体 只在没有参与交互时 才会受重力作用
首先 我将订阅 WillBegin 事件 将 physicsBodyComponent 模式设置为 kinematic 以防止对象在移动时 物理系统发生干扰 这样做还可以防止重力影响实体
接下来 我将订阅 WillEnd 事件 将 physicsBodyComponent 模式改回 dynamic 因为玩家已经没有在与实体交互 这样做将允许实体对场景中的 其他物理对象做出反应 它还会对重力做出反应! 现在 游戏对象已经 可以响应物理作用了 我需要它们与 玩家周围的环境发生碰撞 为此 我可以使用新的 Scene Understanding API 将网格从我的房间 添加到 App 的物理模拟 通过 SpatialTrackingSession API RealityKit 能够生成 周围环境的网格 这个网格称为场景理解网格 可以为房间中的真实物体 添加碰撞和物理作用
你可以利用来自现实环境 的场景理解网格 方法是在 SpatialTrackingSession 配置下 设置 SceneUnderstandingFlags
visionOS 目前支持 collision 和 physics 标志 我将使用这两个标志 来让我的游戏对象 与我们的场景理解网格碰撞 我将在运行 SpatialTrackingSession 配置前 设置这些标志
为此 我需要更新之前设置的 SpatialTrackingSession 我要做的就是在开始会话前 把碰撞和物理场景理解标志 添加到 SpatialTrackingSession 配置 现在 场景理解网格 将参与这个游戏的物理模拟 在我把游戏对象放在 桌子上或地板上时 它们会与环境发生碰撞 现在 我的游戏可以与环境交互了 我还想让它 在视觉上也响应环境 为此 我可以使用 EnvironmentBlendingComponent EnvironmentBlendingComponent 是今年的 RealityKit 更新中 为沉浸式空间 App 设计的新组件
这个组件允许实体被 现实世界中的静态对象遮挡 具有这个组件的实体 实际上是否会被遮挡 部分或完全取决于实体 被现实世界的静态对象遮挡的程度 动态移动的物体 如人和宠物 不会遮挡具有这个组件的对象
如果我想添加这个功能 我所要做的就是添加 EnvironmentBlendingComponent 并设置它被周围环境遮挡的 首选混合模式 现在 如果具有 EnvironmentBlendingComponent 的实体被放在现实世界物体的后面 你会注意到这个实体将被遮挡! 请注意 使用 EnvironmentBlendingComponent 的实体 将被视为背景环境的一部分 会放在场景中的 其他虚拟对象后面 现在 EnvironmentBlendingComponent 已经可以工作了 我可以使用新的 MeshInstancesComponent 在周围的游戏区域添加一些装饰元素 去年我们为 RealityKit 添加了 LowLevelMesh 和 LowLevelTexture API 让大家能更好地控制数据渲染 在今年的 RealityKit 更新中 这种低级别访问权限也推广到了 渲染的另一个方面:实例化 在我的 App 中 我想装饰周围的空间 还要定义一个游戏区域
我可以在周围生成多个 重复的实体来装饰这个空间 但是 要做到这一点 我需要多次克隆我的实体 这将为 ModelComponent 创建许多副本 这可能会导致占用较大的 内存和处理空间 一种更高效、更便捷的方式 是使用新的 MeshInstancesComponent
MeshInstancesComponent 允许使用一个实体 多次绘制一个网格 你只需提供一个转换列表 来绘制网格 在 iOS、iPadOS、macOS 和 Apple tvOS 上 可用 LowLevelBuffer 将渲染数据传递给 CustomMaterial 使每个网格实例看起来都独一无二 除了方便外 MeshInstancesComponent 还可以 减少需要发送到 GPU 的数据量 从而提升性能 绘制重复网格时 无需将模型和材质的多个副本 发送到 GPU MeshInstancesComponent 仅发送一次数据
需要注意的是 使用单个 MeshInstancesComponent 绘制的模型仍被视为 单个实体的一部分 如果使用这个组件 来覆盖大面积区域 可以分解为几个较小的实体 以允许进行剔除 下面介绍如何在代码中使用 MeshInstancesComponent 首先 我需要一个要实例化的网格 为此 我将从 App 的内容包中 加载一个实体 现在 我可以初始化 MeshInstancesComponent 和 LowLevelInstanceData 对象 LowLevelInstanceData 对象包含 单个网格实例的数据 在创建 LowLevelInstanceData 对象时 我需要提供 App 所需的实例数 在这里 我使用 20 来代表 游戏区的粗略近似值 这样不会过于拥挤 接下来 我可以将 LowLevelInstanceData 对象分配给 以要实例化的网格部分 的索引为下标的 MeshInstancesComponent 我知道我要实例化的网格很简单 只有一个网格部分 因此我将 LowLevelDataObject 赋值为 partIndex:0
现在 我可以用 每个网格实例的变换 填充 LowLevelInstanceData 对象
为了打造多样化的装饰 我将为每个实例 使用随机的大小、角度和位置 借助这些值 我可以创建一个 转换矩阵并将它分配给一个实例
现在 将 meshInstancesComponent 添加到我的实体中 这样我的实体在绘制时 它将使用 MeshInstancesComponent 中的数据
这样...... 这款游戏就完成了!
现在我们可以开始游戏 并将它锚定在面前的表面上 我们可以拿起并旋转 游戏区中的物体 找到解锁宝箱的钥匙 我们来简要回顾一下 用于创建这个 App 的新 API 我用了新的 AnchorStateEvent API 来锚定内容 然后 我用 ManipulationComponent 允许与对象交互 我使用了场景理解标志 让游戏实体能够与 场景理解网格发生碰撞 最后我使用了 EnvironmentBlendingComponent 和MeshInstancesComponent 让游戏与现实世界融为一体 接下来 我将分享一些会在今年 添加到 RealityKit 的其他精彩功能 比如支持新的沉浸式媒体 今年我们将推出一个全新的组件 叫做 ImagePresentationComponent 用于在 RealityKit 中呈现图像 它支持三种图像: 传统的 2D 图像和照片 空间照片即来自 iPhone 或 Apple Vision Pro 的 立体照片 以及空间场景 这是一种基于现有 2D 图像或照片 创建的新型 3D 图像
空间场景是基于 2D 图像生成的 具有真实深度的 3D 图像 它们就像一张照片的立体版本 当观看者相对于场景移动头部时 用运动视差来 强调空间场景的景深 空间场景是一种很好的方式 能利用 RealityKit 让现有 2D 照片动起来 无论是在 visionOS 的照片 App 中 还是在你自己的 App 中 下面我来介绍一下 把这三类图像添加到 App 中的代码 首先我将演示如何使用 RealityKit 呈现 2D 图像或照片
我首先要找到一个 2D 照片的 URL 并使用这个 URL 来创建 一个新的图像展示组件 组件的构造器是异步的 因为它将图像加载到 内存中可能需要一点时间 组件初始化后 我可以将它分配给实体 让它在 RealityKit 场景中显示
要呈现空间照片 还需要一个步骤 在将组件设置到实体上之前 需要为组件分配所需的查看模式 可以先查看图像是否支持 再指定所需的查看模式 如果未指定所需的查看模式 或图像不支持这个模式 ImagePresentationComponent 就将以 2D 或单视场查看模式 呈现图像 即使图像是空间照片 要选择展示沉浸式空间照片 请使用 spatialStereoImmersive 的查看模式 每当你用空间照片 创建图像展示组件时 这两种空间立体模式都可以使用 2D 图像和空间照片 都是从磁盘上的文件加载的 将这个图像显示为空间场景 需要几个额外的步骤 因为我们需要先生成空间场景 然后才能呈现它 我来展示一下如何通过代码生成 和呈现空间场景
我们可以使用 2D 图像或空间照片 生成空间场景 如果根据空间照片生成空间场景 空间照片中只有一个通道 将用作转换的 2D 图像 要创建空间场景 无需初始化 直接来自图片 URL 的 ImagePresentationComponent 相反 你可以从 URL 创建 Spatial3DImage 并使用空间 3D 图像来初始化 ImagePresentationComponent 但是 这个组件尚未准备好 作为空间场景来呈现 为此 我们需要先生成场景
我们通过调用空间 3D 图像的 生成方法来实现这一目的 这将在几秒钟内生成空间场景 生成成功后 ImagePresentationComponent 的 availableViewingModes 将更新 以包含 spatial3D 和 spatial3DImmersive 模式 然后 我们可以将其中一个设置为 所需的查看模式 以选择进入窗口 或空间场景的沉浸式呈现 请注意 我们不必 提前生成空间场景 可能需要等到 使用 App 的玩家按下按钮后 就像在“照片”App 中一样 在调用 generate 前 将组件所需的 查看模式设置为 .spatial3D 这样就等于告诉组件要在空间场景 准备就绪后立即展示 这会提示组件在 生成过程中显示进度动画 并在生成完成后 立即显示空间场景 下面是 Apple Vision Pro 上的效果示例 图像展示组件显示的生成动画 与 visionOS 上的 App 一样 最终效果在 3D 中看起来很棒
这里简要总结了使用 ImagePresentationComponent 呈现 2D 图像、 空间照片或空间场景的不同方式 要进一步了解这个组件 请访问 developer.apple.com/cn 查看 “在 RealityKit 中呈现图像” 示例代码
今年的另一项沉浸式媒体更新是 VideoPlayerComponent 开始支持播放多种沉浸视频格式! 它现在支持播放空间视频 包括所有空间类型 支持门户模式和沉浸模式
也支持 Apple Projected Media Profile 视频 例如 180 度视频、360 度视频 和宽视野视频! 用户还可以为 Apple Projected Media Profile 视频配置舒适度设置 RealityKit 将自动调整 播放进行适应
除了 Apple 沉浸视频外 这些视频格式还可以配置成 以多种观看模式播放 如需更深入地了解这些更新 请观看讲座: “支持 visionOS App 播放沉浸视频”
接下来 我给大家介绍 今年的其他一些更新 首先会说一说跟踪式空间配件 接下来 是 SwiftUI 和 RealityKit 集成的最新更新 接着 我将介绍新的实体更新 我会概括介绍 RealityKit 对 AVIF 纹理的支持 然后我会讲一讲新的 悬停效果 groupID 功能 最后是关于 RealityViews 新增的 RealityView 的 后期处理效果 我们马上开始 RealityKit 将增加对 跟踪空间配件的支持 让用户可以在共享空间和全空间 中与你的 App交互 你可以在六个自由度 跟踪空间配件 它们还支持触感反馈 可增强 App 和游戏中的交互体验 要进一步了解如何在 App 中 添加空间配件输入 请观看讲座:“探索 visionOS 上的空间配件输入” 今年 RealityKit 还推出了 一些全新的组件 能进一步优化与 SwiftUI 的集成 ViewAttachmentComponent 支持轻松将 SwiftUI 视图 直接添加到实体中 此外 PresentationComponent 支持在实体中添加模态呈现 比如弹出窗口 此外 新的 GestureComponent 简化了在实体中 添加 SwiftUI 手势的过程 欢迎观看讲座: “搭配使用更出色:SwiftUI 和 RealityKit” 了解有关今年 SwiftUI 和 RealityKit 集成的最新消息!
我们还提供了一种新的实体附加方法 可用于将一个实体 附加到另一个实体的枢轴 这个 API 大大简化了 将网格附加到 动画骨架连接点的过程 以这种方式附加网格 将免除手动对齐网格 并且还可以避免 高开销的分层转换更新 此外 还有一个新的 Entity 构造器 可以用于 从内存中的数据对象加载实体 借助这个新的构造器 你可以从一个在线资源加载 整个 RealityKit 场景或 USD 或是通过网络将它们进行流式传输 新的构造器与现有的实体构造器 支持相同的文件格式
此外 RealityKit 还增加了 对 AVIF 编码纹理的支持 提供类似于 JPEG 的画质 支持 10 位颜色 同时尺寸要小很多 你可以使用 Mac 上的预览 App 或终端中的 usdcrush 导出 启用了这种压缩的 USD 此外 HoverEffectComponent 还迎来了一项新功能:GroupID GroupID 是一种在悬停效果 之间创建关联的方法 任何共享 GroupID 的悬停效果 将同时激活 为悬停效果分配 GroupID 将使你能够完全控制 悬停效果的激活方式 无论它们之间的相对层级如何 通常 悬停效果是按层次结构应用的 例如在示例中 左侧的子实体继承了父实体的效果 但如果实体具有 GroupID 就像右侧示例中的实体 A 和实体 B 那么它们的效果就不会 传播给它们的子实体
今年还有一个很棒的新增功能 就是 RealityView 新增的 后期处理效果 你可以使用 customPostProcessing API 添加自定效果 比如使用 Metal Performance Shaders、 CIFilters 或者你自己的着色器 在 App 中添加辉光效果 iOS、iPadOS、macOS 和 Apple tvOS 都支持这个 API
今年的 RealityKit 更新侧重于 帮助开发者更轻松地 使用 RealityKit 打造 3D 体验 我介绍了如何使用一些新的 RealityKit API 打造一款空间益智游戏 包括如何将游戏锚定到你的环境中 并实现与游戏对象的直观交互 我还讨论了新的沉浸式媒体更新 这些更新使你的 App 能够 直接在 RealityKit 中展示空间内容
然后还介绍了一些其他更新 例如空间配件跟踪、 实体更新和悬停效果 groupsID 借助这些新功能 使用 RealityKit 构建 3D App 将比以往更轻松 期待大家为用户打造卓越体验 感谢观看
-
-
4:33 - Set up SpatialTrackingSession
// Set up SpatialTrackingSession @State var spatialTrackingSession = SpatialTrackingSession() RealityView { content in let configuration = SpatialTrackingSession.Configuration( tracking: [.plane] ) // Run the configuration if let unavailableCapabilities = await spatialTrackingSession.run(configuration) { // Handle errors } }
-
4:34 - Set up PlaneAnchor
// Set up PlaneAnchor RealityView { content in // Set up the SpatialTrackingSession // Add a PlaneAnchor let planeAnchor = AnchorEntity(.plane(.horizontal, classification: .table, minimumBounds: [0.15, 0.15])) content.add(planeAnchor) }
-
5:48 - Handle DidAnchor event
// Handle DidAnchor event didAnchor = content.subscribe(to: AnchorStateEvents.DidAnchor.self) { event in guard let anchorComponent = event.entity.components[ARKitAnchorComponent.self] else { return } guard let planeAnchor = anchorComponent.anchor as? PlaneAnchor else { return } let worldSpaceFromExtent = planeAnchor.originFromAnchorTransform * planeAnchor.geometry.extent.anchorFromExtentTransform gameRoot.transform = Transform(matrix: worldSpaceFromExtent) // Add game objects to gameRoot }
-
7:38 - Set up ManipulationComponent
// Set up ManipulationComponent extension Entity { static func loadModelAndSetUp(modelName: String, in bundle: Bundle) async throws -> Entity { let entity = // Load model and assign PhysicsBodyComponent let shapes = // Generate convex shape that fits the entity model // Initialize manipulation ManipulationComponent.configureEntity(entity, collisionShapes: [shapes]) var manipulationComponent = ManipulationComponent() manipulationComponent.releaseBehavior = .stay entity.components.set(manipulationComponent) // Continue entity set up } }
-
9:28 - Subscribe to willBegin ManipulationEvent
// Subscribe to ManipulationEvents // Update the PhysicsBodyComponent to support movement willBegin = content.subscribe(to: ManipulationEvents.WillBegin.self) { event in if var physicsBody = event.entity.components[PhysicsBodyComponent.self] { physicsBody.mode = .kinematic event.entity.components.set(physicsBody) } }
-
9:29 - Subscribe to willEnd ManipulationEvent
// Subscribe to ManipulationEvents // Update the PhysicsBodyComponent to be a dynamic object willEnd = content.subscribe(to: ManipulationEvents.WillEnd.self) { event in if var physicsBody = event.entity.components[PhysicsBodyComponent.self] { physicsBody.mode = .dynamic event.entity.components.set(physicsBody) } }
-
// Set up Scene understanding mesh collision/physics let configuration = SpatialTrackingSession.Configuration( tracking: [.plane], sceneUnderstanding: [.collision, .physics] )
-
11:56 - Set up EnvironmentBlendingComponent
// Set up EnvironmentBlendingComponent entity.components.set( EnvironmentBlendingComponent(preferredBlendingMode: .occluded(by: .surroundings)) )
-
14:20 - Set up MeshInstancesComponent
// Set up MeshInstancesComponent entity let entity = try await ModelEntity(named:"PebbleStriped.usdz") var meshInstancesComponent = MeshInstancesComponent() let instances = try LowLevelInstanceData(instanceCount: 20) meshInstancesComponent[partIndex: 0] = instances instances.withMutableTransforms { transforms in for i in 0..<20 { let scale: Float = .random(in:0.018...0.025) let angle: Float = .random(in:0..<2) * .pi let position = randomPoint(in: inArea, with: scene) let transform = Transform(scale: .init(repeating: scale), rotation: .init(angle: angle,axis: [0, 1, 0]), translation: position) transforms[i] = transform.matrix } } entity.components.set(meshInstancesComponent)
-
17:36 - Load and display a 2D photo
// Load and display a 2D photo guard let url = Bundle.main.url(forResource: "my2DPhoto", withExtension: "heic") else {a return } let component = try await ImagePresentationComponent(contentsOf: url) let entity = Entity() entity.components.set(component)
-
17:57 - Load and display a spatial photo with windowed presentation
// Load and display a spatial photo with windowed presentation guard let url = Bundle.main.url(forResource: "mySpatialPhoto", withExtension: "heic") else { return } var component = try await ImagePresentationComponent(contentsOf: url) // Discover if the component supports windowed spatial photo presentation. if component.availableViewingModes.contains(.spatialStereo) { component.desiredViewingMode = .spatialStereo } entity.components.set(component)
-
18:22 - Load and display a spatial photo with immserive presentation
// Load and display a spatial photo with immersive presentation guard let url = Bundle.main.url(forResource: "mySpatialPhoto", withExtension: "heic") else { return } var component = try await ImagePresentationComponent(contentsOf: url) // Discover if the component supports immersive spatial photo presentation. if component.availableViewingModes.contains(.spatialStereoImmersive) { component.desiredViewingMode = .spatialStereoImmersive } entity.components.set(component)
-
18:56 - Load a spatial photo and use it to generate and present a spatial scene
// Load a spatial photo and use it to generate and present a spatial scene guard let url = Bundle.main.url(forResource: "mySpatialPhoto", withExtension: "heic") else { return } let spatial3DImage = try await ImagePresentationComponent.Spatial3DImage(contentsOf: url) var component = ImagePresentationComponent(spatial3DImage: spatial3DImage) try await spatial3DImage.generate() // Discover if the component supports windowed spatial scene presentation. if component.availableViewingModes.contains(.spatial3D) { component.desiredViewingMode = .spatial3D } entity.components.set(component)
-
20:06 - Generating a spatial scene as needed
// Load a spatial photo and use it to generate and present a spatial scene guard let url = Bundle.main.url(forResource: "mySpatialPhoto", withExtension: "heic") else { return } let spatial3DImage = try await ImagePresentationComponent.Spatial3DImage(contentsOf: url) var component = ImagePresentationComponent(spatial3DImage: spatial3DImage) component.desiredViewingMode = .spatial3D // (or .spatial3DImmersive) entity.components.set(component) try await spatial3DImage.generate()
-
23:35 - Load entity from Data object
// Load entity from Data object if let (data, response) = try? await URLSession.shared.data(from: url) { if let entity = try? await Entity(from: data) { content.add(entity) } }
-
-
- 0:00 - 简介
RealityKit 框架于 2019 年推出,让开发者能够在各种 Apple 平台上创作沉浸式 3D App 和游戏,这些平台涵盖 visionOS、iOS、iPadOS、macOS 以及现在的 Apple tvOS。 基于用户反馈,最新更新引入了一些新功能,包括:可直接访问 ARKit 数据;可为 3D 实体添加交互操作的“ManipulationComponent”;可实现无缝遮挡现实世界的“EnvironmentBlendingComponent”;以及可用于高效渲染多个 3D 模型的“MeshInstancesComponent”。 这些功能通过一款空间益智游戏得到完美展现。玩家可在这款游戏中与物体交互以解锁宝箱,生动演示了 RealityKit 如何实现虚拟与现实体验的无缝融合。
- 3:19 - 锚定方面的更新
借助这个新的 API,你可以通过 RealityKit 直接访问 ARKit 数据,从而进一步增强“AnchorEntities”的功能。通过创建启用了平面跟踪的“SpatialTrackingSession”,你可以设置“AnchorEntities”来筛选特定的现实世界表面,比如符合最小尺寸要求的桌面。 当系统检测到符合条件的表面时,将自动触发“AnchorStateEvent”并提供 ARKit 数据,比如转换和范围。这些数据随后可以用于在锚定表面上精准地放置实体,确保它们居中显示且与观看者视角对齐。
- 6:52 - ManipulationComponent
在沉浸式空间中,游戏物体会自动生成在符合条件的表面上。借助 RealityKit 中的“ManipulationComponent”,用户能够自然地抓取、旋转并在双手间交换这些 3D 实体。 调用“configureEntity”函数会自动添加必要的组件。为避免物体释放后重置位置,可将“releaseBehavior”属性设置为“stay”。 新增的“PhysicsBodyComponent”使物体能自然坠落至地面。“ManipulationEvents”API 可用于切换重力的开启与关闭状态;当用户释放物体时 (“WillEnd”事件),重力状态将开启,而当用户拾取物体时 (“WillBegin”事件),重力状态将关闭。这样既能保证操作过程中的精准控制,又能实现更真实的交互效果。
- 10:01 - 场景理解
在 visionOS 中,借助 RealityKit 的 Scene Understanding API,你可以将真实世界环境融入游戏的物理模拟中。通过使用“SpatialTrackingSession”API,你可以生成周围环境的网格。为了让游戏物体与这个真实世界网格发生碰撞,需要在启动会话之前,在“SpatialTrackingSession”配置中预先设置可实现碰撞和物理特性的“SceneUnderstandingFlags”。此项更新可实现游戏物体与物理环境的真实交互,例如将物体自然坠落在桌面或地板上。
- 11:18 - EnvironmentBlendingComponent
借助最新的 RealityKit 更新,你可以在沉浸式空间 App 中使用“EnvironmentBlendingComponent”,以实现现实世界环境遮挡虚拟实体的效果。添加该组件并设置混合模式后,现实世界中的静态物体就能完美遮挡虚拟实体,让虚实融合浑然一体。但是,需要注意的是,这些实体始终会显示在场景中其他虚拟物体的后方,并且无法被人或宠物等动态移动的物体所遮挡。
- 12:26 - MeshInstancesComponent
最新的 RealityKit 更新还引入了“MeshInstancesComponent”,该组件让你能够在实体中多次绘制单个网格,从而提高渲染效率。这个新组件省去了复制实体的步骤,可大幅降低内存占用和运算负荷。 使用“MeshInstancesComponent”,你可以为每个网格实例提供一个转换列表,在支持的平台上通过“CustomMaterials”实现独特的外观。此方法通过优化 GPU 数据传输来提高性能。 在将此组件应用于大范围区域时,为了实现正确剔除,可将其分解为多个较小的实体。演示中展示了如何用随机化的转换来初始化并填充“MeshInstancesComponent”,从而生成一个带有装饰物的游戏区域。 搭配使用“AnchorStateEvent”、“ManipulationComponent”、SceneUnderstandingFlags 和“EnvironmentBlendingComponent”等 API,能够实现交互式、融合真实世界的 (AR) 体验。
- 16:28 - 沉浸式媒体
RealityKit 今年将进行重大更新,以增强对沉浸式媒体的支持。使用新组件“ImagePresentationComponent”,可以显示三种类型的图像:传统的 2D 图像、空间照片和空间场景。 空间场景是一个值得关注的新功能。它们是由 2D 照片生成的 3D 图像,通过运动视差,营造出类似立体的视觉效果,使静态图像更加生动、更具吸引力。要呈现空间场景,你可使用“Spatial3DImage”类基于 2D 图像或空间照片生成它们,然后设置所需的观看模式。 “VideoPlayerComponent”也已更新,可支持各种沉浸式视频格式,包括空间视频、Apple Projected Media Profile 视频 (如 180°和 360°视频) 以及 Apple 沉浸视频。你可以在不同的观看模式下播放这些视频,还可以为 Apple Projected Media Profile 视频配置舒适度设置。
- 21:43 - 配件及其他
今年的 RealityKit 更新大幅提升了 3D App 的开发效率。主要更新包括以下内容。 空间配件:RealityKit 现在支持以六个自由度跟踪空间配件,并结合触觉反馈,从而在“共享空间”和“全空间”中实现更具沉浸感的交互体验。 SwiftUI 整合:“ViewAttachmentComponent”、“PresentationComponent”和“GestureComponent”等新组件可简化向实体添加 SwiftUI 视图、模态呈现和手势的过程。 实体更新:全新实体附加方法可简化将网格附加到动画骨架的过程,并且通过构造器可从内存数据对象加载实体,支持在线场景和 USD 流式传输。 纹理和悬停效果支持:RealityKit 现已支持 AVIF 编码纹理,可在保证画质的前提下缩减文件大小。“HoverEffectComponent”新增“GroupID”,可精准控制悬停效果的激活。 后处理效果:“RealityViews”现在支持后处理效果,你可使用 Metal 性能着色器、CIFilter 或自定着色器,在 iOS、iPadOS、macOS 和 Apple tvOS 全平台添加辉光等自定效果。