背景
在体验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
来实现光照。