VisionPro开发系列首页:漫游Apple Vision Pro
系列项目全部代码:github.com/xuchi16/vis...
大家好,我是 xChester。
当我们希望给用户提供沉浸式体验时,常需要利用环境贴图构造天空盒。而 visionOS 本身不提供天空盒的功能,本文将实现一个简单的天空盒效果。
本文主要包含以下内容:
-
简单的天空盒效果(Skybox),提供沉浸的体验
-
为其中的物体添加基于图形的光照(Image Based Lighting,IBL),提供反射效果。
最终效果如下,可以看到沉浸式的天空和,同时其中的物体有相应的反射效果。
基本概念
HDRI(High Dynamic Range Imaging,高动态范围成像)环境贴图是一种提供详细光照信息的全景图像技术,用于在 3D 渲染和视觉效果中创建更加真实的光照和反射效果。
简单来说,当我们使用一张普通的全景图片作为环境贴图时,由于其中仅包含了拍摄时的曝光信息,当后续 3D 场景中渲染光照发生变化时图片中无论亮部还是暗部都会统一变化,如当光照变暗时,普通的图片会显得更"灰"。而相比于普通的全景图像,HDRI 图像在拍摄时就会对环境进行多次曝光,生成一系列曝光度不同的照片,并将它们合并成一个动态范围更广的图像。其中包含了更丰富的光照信息,包括色彩、亮度和反射细节等,在 3D 场景和模型的构建中能够营造更为真实的效果。
左。 HDR / 右。 JPG
IBL(Image Based Lighting)是一种在三维图形和视觉效果中使用的照明技术,通过使用 HDRI 图片来模拟光照,从而创造出更加真实和动态的视觉效果。RealityKit 就使用了 IBL 技术来模拟真实的反射效果。
主要步骤
-
获取 HDRI 资源并创建材质
-
创建模拟天空盒的球体并将 HDRI 贴图材质应用到天空盒上
-
加载 HDRI 资源并创建环境资源
-
创建环境中的物体并使用 IBL 光照
基本实现
获取 HDRI 资源并创建材质
从PolyHeaven中找到想要的 HDRI 图片并下载,我们这里使用了一张上海外滩的夜景图。下载该图并导入 Xcode 项目中。利用如下代码
- 创建
TextureResource
- 创建
UnlitMaterial
并使用上述贴图
UnlitMaterial
是一种简单的材料,不会对环境中的其他光源做出响应。
swift
guard let resource = try? await TextureResource(named: "shanghai_bund_4k") else {
fatalError("Unable to load texture.")
}
var material = UnlitMaterial()
material.color = .init(texture: .init(resource))
创建天空盒球体
创建一个半径为 1000 的巨大球体,并将上述贴图应用到该球体上。另外,需要指定其 scale 的 x 方向为负,这样可以将贴图材质应用到球体的内表面。此时运行程序,我们仿佛已经置身上海外滩。
swift
skybox.components.set(ModelComponent(
mesh: .generateSphere(radius: 1000),
materials: [material]
))
// Reverse x to let the picture applied to the inner side of skybox
skybox.scale = .init(x: -1 * abs(skybox.scale.x), y: skybox.scale.y, z: skybox.scale.z)
加载 HDRI 资源并创建环境资源
但如果此时在场景中添加物体,会发现物体并没有反射天空盒中的景色,呈现出黑色的无光照效果。
为了给物体添加 IBL 光照,仍需要使用刚刚的 HDRI 环境贴图。在 RealityKit 中,想要将该图用作 IBL 资源,需要
-
创建一个文件夹,并将其后缀改为
.skybox
-
将 HDRI 文件放入上述文件夹中
-
将该文件夹拖到 Xcode 项目中,并选择"Create folder reference",将其添加到项目中
这样 Xcode 就能够将这张图片编译为环境资源,可以在代码中获取到该资源。
swift
guard let environment = try? await EnvironmentResource(named: "shanghai_bund_4k") else {
return
}
创建环境中的物体并使用 IBL 光照
创建一个球体,并将球体颜色设置为白色,isMetallic
设置为ture
,模仿光滑的金属表面,从而获得更好的反射效果。
swift
let sphere = ModelEntity(
mesh: .generateSphere(radius: 0.25),
materials: [SimpleMaterial(color: .white, isMetallic: true)]
)
然后,创建一个ImageBasedLightComponent
,通过该组件引用上述环境资源。再创建一个ImageBasedLightReceiverComponent
作为关键光照的接收组件。将上述两组件应用到球体上,即可获得逼真的光照反射效果。
swift
sphere.components.set(ImageBasedLightComponent(source: .single(environment)))
sphere.components.set(ImageBasedLightReceiverComponent(imageBasedLight: sphere))
最终效果
关注我
欢迎在掘金上关注我和我的专栏VisionOS Workshop,以及各种收藏/围观/评论/反馈/批评/Star/点歌