为 visionOS 创建 Environment / 虚拟环境
学习如何使用全景图片或 360° 图片,为 visionOS 创建沉浸式的虚拟环境。
对于开发 visionOS 平台的应用,创建一个逼真且具有沉浸感的 Environment 是至关重要的。
这篇文章将向你介绍,如何使用 RealityKit 结合全景图片或 360° 图片创建一个栩栩如生的虚拟环境,打造出令人惊叹的视觉效果。
使用 RealityKit 创建虚拟环境
创建沉浸式的虚拟环境有多种方法,例如使用Blender等建模软件进行详细的3D建模,或通过 Skybox 技术创建背景和环境效果,还可以利用 Unity 等其他技术栈。
使用 Skybox 技术创建虚拟环境的优势是,你只需要使用 RealityKit 通过编程的方式来实现,这非常的简单和高效,特别适用于快速开发和原型设计。
可以实现什么效果?
可以创建一个 360 度环绕的沉浸式虚拟环境,可以通过加载不同的图片资源,快速切换不同的环境效果:


什么是 Skybox 技术
Skybox 技术是一种在 3D 图形和游戏中经常使用的背景渲染方法,广泛应用于各种图形和游戏引擎中(例如 Unity、Unreal Engine),用于创建逼真的背景和环境效果。

想象一下,你站在一个立方体的中央,立方体的每个面都贴着一个大图像,这些图像连接在一起形成一个完整的全景画面。无论你往哪个方向看,看到的都是连续的背景图像,没有边界或空隙。
Skybox 是通过将场景包围在一个立方体内,并在其六个面上贴上预渲染的图像,从而模拟出一个无缝的背景环境,这就是 Skybox 技术的工作原理。

在游戏或虚拟现实中,Skybox 技术常用于创造逼真的天空或远景效果,让你感觉自己处在一个真实的环境中,而不是一个空荡荡的虚拟空间。
在 RealityKit 中使用 Skybox
截止 2024 年 6 月,苹果并未在 RealityKit 中直接提供用于实现 Skybox 的官方 API。因此,我们需要自己实现一个 createSkyBox 方法,好在这并不复杂。
注意:RealityKit 虽然提供了 skybox(_:) 方法,但目前并不适用于 visionOS:

创建 createSkyBox() 方法,该方法返回一个 Entity 对象,它的工作流程如下:
- 使用 MeshResource 创建一个球体形状
- 使用 SimpleMaterial 添加材质
- 最后,通过 Entity 的 .components.set() 方法,将 Mesh 和 Material 组合创建一个 Entity 对象。
首先,你需要理解 RealityKit 中的基本概念:
在 RealityKit 中,Entity 是一个抽象的容器。它本身不具备任何属性,但可以包含各种组件(Component)。这些组件定义了 Entity 的外观、行为以及其他特性。
其中一个组件是 ModelComponent,这个组件定义了 Entity 的几何形状(Mesh)和材质(Material)。为了创建 ModelComponent 组件并添加到 Entity 中,我们首先需要定义 Mesh 和 Material。
我们一起来看看如何实现这个方法。
- 创建 Mesh 几何图形
//Mesh
let skyBoxMesh = MeshResource.generateSphere(radius: 1000)
MeshResource 是 RealityKit 提供用于生成几何形状的类,.generateSphere(radius:) 是这个类的方法之一,用于生成一个球体网格。最符合我们创建 skybox 的需求。
为了创建一个尽可能大的空间,因此我们通过 radius: 1000 指定了生成球体的半径为 1000 米。
- 创建 Material 材质
//Material
let skyBoxMaterial = SimpleMaterial(color: .systemPink, isMetallic: false)
RealityKit 提供了几种用于创建 Material 的方法,最常用的是 SimpleMaterial() 和 UnlitMaterial()。
上述代码使用 SimpleMaterial() 创建了一个简单的材质,color: .systemPink 指定了材质的颜色为系统粉红色 (systemPink),isMetallic: false 指定材质不具有金属光泽。
- 创建 ModelComponent 组件
//ModelComponent
let modelComponent = ModelComponent(
mesh: skyBoxMesh,
materials: [skyBoxMaterial]
)
ModelComponent 是 RealityKit 中的一个组件类,负责定义实体的几何形状和外观。它包含网格(Mesh)和材质(Materials)等属性。通过 mesh 和 materials 参数,我们直接调用了刚才创建的 skyBoxMesh 和 skyBoxMaterial。
注意: materials 参数是一个数组,因此你必须使用 [] 来赋值。
- 添加 ModelComponent 到 Entity
//Entity
let skyBoxEntity = Entity()
skyBoxEntity.components.set(modelComponent)
首先创建一个 skyBoxEntity,这个 Entity 默认不具有任何内容。然后使用.components.set() 方法将 modelComponent 添加到 skyBoxEntity。
完整代码如下:
// create a createSkyBox function which return a Entity object
func createSkyBox() -> Entity? {
//Mesh
let skyBoxMesh = MeshResource.generateSphere(radius: 1000)
//Material
let skyBoxMaterial = SimpleMaterial(color: .systemPink, isMetallic: false)
//ModelComponent
let modelComponent = ModelComponent(
mesh: skyBoxMesh,
materials: [skyBoxMaterial]
)
//Entity
let skyBoxEntity = Entity()
skyBoxEntity.components.set(modelComponent)
//reverse texture surface
skyBoxEntity.scale *= .init(x: -1, y: 1, z: 1)
return skyBox
}
我们还额外添加了一段代码来反转球体的材质表面,使其从内部显示正确的图像:
//reverse texture surface
skyBoxEntity.scale *= .init(x: -1, y: 1, z: 1)
这段代码将 skyBoxEntity 球体的材质内外反转。由于创建的 Skybox 球体默认情况下材质显示在外部,所以需要反转纹理表面,以确保图像从内部看起来正确。通过这种方式,我们成功创建了一个简单的球体,作为环绕虚拟环境的 Skybox。
在之前的示例中,我们使用 SimpleMaterial
方法将材质设置为简单的粉红色:
let skyBoxMaterial = SimpleMaterial(color: .systemPink, isMetallic: false)
因此,您会看到整个空间都是粉红色的:

下一步,我们将通过使用 UnlitMaterial
和加载实际的纹理图像,创建出更为真实和吸引人的虚拟环境。
使用图片为 Skybox 添加纹理
在上一步的演示代码中,我们通过使用 SimpleMaterial() 创建了一个简单的材质,这个材质仅包含简单的颜色:
let skyBoxMaterial = SimpleMaterial(color: .systemPink, isMetallic: false)
这显然并不如何吸引人,我们可以使用 UnlitMaterial() 来添加更加丰富的 Texture 纹理,从而创建出更加真实的空间场景:

在 RealityKit 中,实体(Entity)的外观由包含网格(Mesh)和材质(Material)的模型组件(ModelComponent)定义。材质可以包含纹理,从而增强视觉效果。
接下来,我们将之前使用的 SimpleMaterial 修改为 UnlitMaterial 实例,UnlitMaterial 支持自定义纹理效果:
// 创建 UnlitMaterial
var skyBoxMaterial = UnlitMaterial()
然后,我们需要创建一个 Texture 资源,并将它添加到 skyBoxMaterial 中。
在 Xcode 项目的 Assets 中,创建一个新的 Image Set,并添加一张全景照片或 360° 照片,这将作为纹理来加载:

如果你还没有全景照片或 360° 照片资源,可以查看我的下面这篇文章,详细介绍了如何获取免费的高清全景照片和 360° 照片:
使用 RealityKit 提供了 TextureResource 类来创建纹理资源,TextureResource.load(named:) 方法可以加载一个静态资源(例如图片)来创建纹理,下面代码将加载一个名为 shybox04-360 的 Image Set 作为纹理:
// 加载纹理
let skyBoxTexture = try TextureResource.load(named: "skybox04-360")
skyBoxMaterial.color = .init(texture: .init(skyBoxTexture))
由于 TextureResource.load(named:) 是同步加载,因此需要放在 do-catch 语句中以处理可能的错误:
// 加载纹理
do {
let skyBoxTexture = try TextureResource.load(named: "skybox04-360")
skyBoxMaterial.color = .init(texture: .init(skyBoxTexture))
} catch {
print("Failed to load skybox texture: \(error)")
return nil
}
最终的完整代码:

可以通过导入不同的纹理图片,创建出不同的环境效果:

在模拟器或 Vision Pro 上运行代码,体验您亲手创建的沉浸式环境效果吧!
图片资源
免费下载高清全景图片/360° 图片
如果你还没有全景照片或 360° 照片资源,可以查看我的下面这篇文章,详细介绍了如何获取免费的高清全景照片和 360° 照片:
图片分辨率应当小于 8192
由于硬件性能的限制,Apple 目前允许加载的最大分辨率为 8192 x 8192,所以你在选择图片的时候需要注意这一点。
否则会出现虚拟环境无法加载或闪退,并在 Xcode 会出现 failed assertion `Texture Descriptor Validation 的错误:


参考资源
