背景
在体验HelloWorld时,很好奇每个功能是怎么实现的,但是这个demo复用了很多功能、数据模型,刚开始理解起来就比较困难。所以我就先从功能点来看,将复用的功能、数据模型都剔除掉,保证单一功能能解藕单独运行。
环境
Xcode:15.1 beta
VisionOS:1.0
梳理功能
设置光照
我是把官方demo中的功能单独拆分出来,方便学习。

swift
import SwiftUI
import RealityKit
import RealityKitContent
struct EarthSetSunlight: View {
@State var intensity: Float = 14
@State var showSunlight = false
@State var curEarth:Entity?
var body: some View {
ZStack{
RealityView { content in
guard let earth = await RealityKitContent.entity(named: "Earth") else {
return
}
content.add(earth)
earth.scale = SIMD3(repeating: 0.3)
curEarth = earth
} update: { content in
curEarth?.setSunlight(intensity: showSunlight ? intensity : 8)
}
Toggle("Sunlight", isOn: $showSunlight)
.toggleStyle(.button)
.padding(.top, 240)
}
}
}
#Preview {
EarthSetSunlight()
}
这段代码很简单,就是一个3D模型、一个切换开关,开关控制是否要设置光照。
这里有两个知识点:
1. 加载3D资源
1.1 资源文件
1.1.1 直接拖HelloWorld里面的资源到自己的项目


将所有的资源添加到自己的工程里面默认的RealityKitContent.rkassets文件夹下面
1.1.2 自己创建
创建一个新的visionos工程会自带一个3D资源。

如果要编辑资源,可以打开Package,点击右上角Open in Reality Composer Pro

打开后的样子

在Reality Composer Pro里面添加一个地球3D模型资源。


双击资源,默认会把资源添加到Scene,资源叫Earth.usdz
也可以拖动资源到下面的资源栏,这样就不属于任何scene。

如果不想放在原有的Scene,就可以新建一个scene叫Earth

将Earth.usdz资源拖动到Earth scene里面,就生成了一个Earth.usda文件,这个就是后续RealityView加载的资源。

1.2 加载资源
目前加载3D资源,我这边统一用的是RealityKit,平常我接触到的有两个Model3D、RealityView。
Model3D更简单一点,拿来即用,就像加载Image一样。
RealityView自定义的程度更高一些,主要用来展示Entity的。可以编辑资源Entity,还可以在Entity添加各种功能的组件Component。还可以根据属性变化,来更新Entity。
Entity就是3D资源对象,可以改变它的大小、位置、旋转等,还可以添加各种功能的Component,包括手势、碰撞、粒子效果、光照效果等。
2 添加光照
经过上面的介绍,光照其实就是一个功能Component,添加到了3D资源对象上了。
2.1 光照资源
Swift
import SwiftUI
import RealityKit
extension Entity {
/// Adds an image-based light that emulates sunlight.
///
/// This method assumes that the project contains a folder called
/// `Sunlight.skybox` that contains an image of a white dot on a black
/// background. The position of the dot in the image dictates the direction
/// from which the sunlight appears to originate. Use a small dot
/// to maximize the point-like nature of the light source.
///
/// Tune the intensity parameter to get the brightness that you need.
/// Set the intensity to `nil` to remove the image-based light (IBL)
/// from the entity.
///
/// - Parameter intensity: The strength of the sunlight. Tune
/// this value to get the brightness you want. Set a value of `nil` to
/// remove the image based light from the entity.
func setSunlight(intensity: Float?) {
if let intensity {
Task {
guard let resource = try? await EnvironmentResource(named: "Sunlight") else { return }
var iblComponent = ImageBasedLightComponent(
source: .single(resource),
intensityExponent: intensity)
// Ensure that the light rotates with its entity. Omit this line
// for a light that remains fixed relative to the surroundings.
iblComponent.inheritsRotation = true
components.set(iblComponent)
components.set(ImageBasedLightReceiverComponent(imageBasedLight: self))
}
} else {
components.remove(ImageBasedLightComponent.self)
components.remove(ImageBasedLightReceiverComponent.self)
}
}
}
注释上说的很清楚了,就是Sunlight.skybox文件夹下面有一个图片,图片黑色背景,有一个光点,光点就代表了光源的位置,物体的位置可以理解成是正中心。


红色就表示我们要设置光照的位置,光点就是光源的位置,这里就是光源在左边。
2.2 添加Component
Entity对象可以添加多个Component,来实现不同的效果,这里就添加的是ImageBasedLightComponent、ImageBasedLightReceiverComponent来实现光照。