布景

在体会HelloWorld时,很猎奇每个功用是怎样完成的,但是这个demo复用了很多功用、数据模型,刚开始了解起来就比较困难。所以我就先从功用点来看,将复用的功用、数据模型都剔除掉,保证单一功用能解藕独自运转。

环境

Xcode:15.1 beta

VisionOS:1.0

整理功用

graph LR;
    功用点-->A(设置光照);
    style A fill:#bbf,color:#fff
    click A "https://www.6hu.cc/post/7298690615046651943"
    功用点-->B(手势转动地球)
    style B fill:#bbf,color:#fff
    click B "https://www.6hu.cc/post/7298765809290706983"
    功用点-->C(地球自转)
    功用点-->D(地球跟从鼠标拖动)
    功用点-->E(卫星围绕地球转动)
    功用点-->F(月球围绕地球转动)
    功用点-->G(沉溺式与窗口之间的切换)

设置光照

我是把官方demo中的功用独自拆分出来,便利学习。

[VisionOS] 拆分HelloWorld的功用点 - 设置光照

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里边的资源到自己的项目

[VisionOS] 拆分HelloWorld的功用点 - 设置光照

[VisionOS] 拆分HelloWorld的功用点 - 设置光照

将所有的资源增加到自己的工程里边默许的RealityKitContent.rkassets文件夹下面

1.1.2 自己创立

创立一个新的visionos工程会自带一个3D资源。

[VisionOS] 拆分HelloWorld的功用点 - 设置光照

假如要修改资源,能够打开Package,点击右上角Open in Reality Composer Pro

[VisionOS] 拆分HelloWorld的功用点 - 设置光照

打开后的样子

[VisionOS] 拆分HelloWorld的功用点 - 设置光照

Reality Composer Pro里边增加一个地球3D模型资源。

[VisionOS] 拆分HelloWorld的功用点 - 设置光照

[VisionOS] 拆分HelloWorld的功用点 - 设置光照

双击资源,默许会把资源增加到Scene,资源叫Earth.usdz

也能够拖动资源到下面的资源栏,这样就不属于任何scene。

[VisionOS] 拆分HelloWorld的功用点 - 设置光照

假如不想放在原有的Scene,就能够新建一个scene叫Earth

[VisionOS] 拆分HelloWorld的功用点 - 设置光照

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

[VisionOS] 拆分HelloWorld的功用点 - 设置光照

1.2 加载资源

目前加载3D资源,我这边统一用的是RealityKit,往常我接触到的有两个Model3DRealityView

Model3D更简略一点,拿来即用,就像加载Image一样。

RealityView自定义的程度更高一些,主要用来展示Entity的。能够修改资源Entity,还能够在Entity增加各种功用的组件Component。还能够根据属性变化,来更新Entity

Entity就是3D资源目标,能够改变它的大小、方位、旋转等,还能够增加各种功用的Component,包括手势、碰撞、粒子作用、光照作用等。

2 增加光照

经过上面的介绍,光照其实就是一个功用Component,增加到了3D资源目标上了。

2.1 光照资源

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文件夹下面有一个图片,图片黑色布景,有一个光点,光点就代表了光源的方位,物体的方位能够了解成是正中心。

[VisionOS] 拆分HelloWorld的功用点 - 设置光照

[VisionOS] 拆分HelloWorld的功用点 - 设置光照

红色就表示我们要设置光照的方位,光点就是光源的方位,这儿就是光源在左边。

2.2 增加Component

Entity目标能够增加多个Component,来完成不同的作用,这儿就增加的是ImageBasedLightComponentImageBasedLightReceiverComponent来完成光照。