• visionOS空间计算实战开发教程Day 5 纹理和材质


    在​​Day 4​​​中我们使用了​​ImmersiveSpace​​并在其中添加了一个立方体,但对这个立方体我们只配置了长宽高,并没有做进一步的操作。

    本文中我们会通过纹理和材质对这个立方体的六个面分别进行不同的绘制。首先我们将​​ImmersiveView​​分拆出来,先新建一个​​ImmersiveView.swift​​文件,这是一个视图文件,所以请选择User Interface下的Swift View完成创建,其中的内容待我们编写完​​ViewModel​​中的代码后再进行修改。

    我们通过点击界面来打开沉浸式视图,所以需要一个​​ContentView.swift​​文件来编写常规的窗口页面,在其中添加一个Toggle开关,用于打开和关闭沉浸式视图。

    1. import SwiftUI
    2. import RealityKit
    3. struct ContentView: View {
    4. @State var showImmsersiveSpace = false
    5. @Environment(\.openImmersiveSpace) var openImmersiveSpace
    6. @Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace
    7. var body: some View {
    8. NavigationStack {
    9. VStack {
    10. Toggle("Show ImmersiveSpace", isOn: $showImmsersiveSpace)
    11. .toggleStyle(.button)
    12. }
    13. .padding()
    14. }
    15. .onChange(of: showImmsersiveSpace) { _, newValue in
    16. Task {
    17. if newValue {
    18. await openImmersiveSpace(id: "ImmersiveSpace")
    19. } else {
    20. await dismissImmersiveSpace()
    21. }
    22. }
    23. }
    24. }
    25. }
    26. #Preview {
    27. ContentView()
    28. }

    首先我们定义了一个​​showImmsersiveSpace​​变量供Toggle按钮开关时使用。然后要打开和关闭沉浸式空间,我们可以分别使用​​@Environment​​中的​​openImmersiveSpace​​和​​dismissImmersiveSpace​​,通过​​onChange​​修饰符来监控​​showImmsersiveSpace​​变量的变化,在切换到打开时,就打开沉浸式空间。我们需要知道打开哪一个视图,所以使用了​​id​​参数,这个参数应与应用入口文件中所设置的一致。

    接下来就编写入口文件:

    1. import SwiftUI
    2. @main
    3. struct visionOSDemoApp: App {
    4. var body: some Scene {
    5. WindowGroup() {
    6. ContentView()
    7. }
    8. ImmersiveSpace(id: "ImmersiveSpace") {
    9. ImmersiveView()
    10. }
    11. }
    12. }

    注意这里​​ImmersiveSpace​​中所添加的​​id​​与之前​​ContentView​​中的要保持一致,我们在后面会学到在同一个应用中也可以添加多个​​WindowGroup​​,同样通过配置​​id​​进行区分。

    接下来是核心文件​​ViewModel.swift​​,

    1. import SwiftUI
    2. import RealityKit
    3. @MainActor class ViewModel: ObservableObject {
    4. private var contentEntity = Entity()
    5. func setupContentEntity() -> Entity {
    6. return contentEntity
    7. }
    8. func addCube() {
    9. guard
    10. let texture1 = try? TextureResource.load(named: "Number_1"),
    11. let texture2 = try? TextureResource.load(named: "Number_2"),
    12. let texture3 = try? TextureResource.load(named: "Number_3"),
    13. let texture4 = try? TextureResource.load(named: "Number_4"),
    14. let texture5 = try? TextureResource.load(named: "Number_5"),
    15. let texture6 = try? TextureResource.load(named: "Number_6")
    16. else {
    17. fatalError("Unable to load texture.")
    18. }
    19. let entity = Entity()
    20. var material1 = SimpleMaterial()
    21. var material2 = SimpleMaterial()
    22. var material3 = SimpleMaterial()
    23. var material4 = SimpleMaterial()
    24. var material5 = SimpleMaterial()
    25. var material6 = SimpleMaterial()
    26. material1.color = .init(texture: .init(texture1))
    27. material2.color = .init(texture: .init(texture2))
    28. material3.color = .init(texture: .init(texture3))
    29. material4.color = .init(texture: .init(texture4))
    30. material5.color = .init(texture: .init(texture5))
    31. material6.color = .init(texture: .init(texture6))
    32. entity.components.set(ModelComponent(
    33. mesh: .generateBox(width: 0.5, height: 0.5, depth: 0.5, splitFaces: true),
    34. materials: [material1, material2, material3, material4, material5, material6]
    35. ))
    36. entity.position = SIMD3(x: 0, y: 1, z: -2)
    37. contentEntity.addChild(entity)
    38. }
    39. }

    同样我们创建了​​setupContentEntity()​​方法,但并没有在方法里添加任何模型,而是将添加的工作交给了​​addCube()​​方法,整个方法虽然很长,但有大量重复的代码,分别为六个面设置不同的图片。

    1. 通过​​TextureResource.load()​​方法设置了不同的纹理
    2. 接着使用​​SimpleMaterial()​​创建了六个材质
    3. 通过材质的​​color​​属性分别添加前面配置好的纹理

    ​Number_1.jpg​​等图片请见我们GitHub的演示代码,我们先来说一下visionOS中的材质,常见的材质请见下图:

    材质 Material

    其中​​PhysicallyBasedMaterial​​也即PBR和​​SimpleMaterial​​是带光照信息的,而​​UnlitMaterial​​和​​VideoMaterial​​都是不带光照信息的。

    在​​ModelComponent​​方法中,我们使用了​​mesh​​和​​materials​​参数,​​mesh​​参数我们同样使用了​​MeshResource.generateBox​​来创建一个立方体,不同的是这次我们指定了​​splitFaces​​参数,该参数设为​​true​​表示顶点不进行合并,因为我们要对六个面分别设置颜色或图像,不指定该参数六个面都会使用相同的材质,在本例中也就是都显示为​​1​​。

    最后我们配置了​​position​​,这里x, y, z坐标轴方向示意如下:

    visionOS坐标轴方向

    接下来我们修改​​ImmersiveView.swift​​的内容如下:

    1. import SwiftUI
    2. import RealityKit
    3. struct ImmersiveView: View {
    4. @StateObject var model = ViewModel()
    5. private var contentEntity = Entity()
    6. var body: some View {
    7. RealityView { content in
    8. content.add(model.setupContentEntity())
    9. model.addCube()
    10. }
    11. }
    12. }

    这里的代码相对简单,就是在​​RealityView​​中展示​​ViewModel​​中所添加的模型。

    在运行应用前将Info.plist文件中的Preferred Default Scene Session Role切换回Window Application Session Role

    点击Show ImmersiveSpace按钮,会得到如下界面:

    Show ImmersiveSpace

    再次点击按钮就会收起这个模型。

    示例代码:​​GitHub仓库​

    其它相关内容请见​​虚拟现实(VR)/增强现实(AR)&visionOS开发学习笔记​

  • 相关阅读:
    python使用MQTT协议详解
    PHP:类型转换
    Mac终端出现 brew command not found 解决
    Profile注解
    免费小程序商城搭建之b2b2c o2o 多商家入驻商城 直播带货商城 电子商务b2b2c o2o 多商家入驻商城 直播带货商城 电子商务
    Java项目论文+PPT+源码等]S2SH+mysql水费管理系统
    【人工智能】第三部分:ChatGPT的应用场景和挑战
    用C语言随机读写二进制文件
    ElasticSearch - ​开启搜索的新境界
    【ARXML专题】-26-Bit Rate相关参数:Tq,SJW,Sample Point,TDC...的定义
  • 原文地址:https://blog.csdn.net/ardor123/article/details/134545060