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

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

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

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

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

import SwiftUI
import RealityKit
struct ContentView: View {
    @State var showImmsersiveSpace = false
    @Environment(\.openImmersiveSpace) var openImmersiveSpace
    @Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace
    var body: some View {
        NavigationStack {
            VStack {
                Toggle("Show ImmersiveSpace", isOn: $showImmsersiveSpace)
                    .toggleStyle(.button)
            }
            .padding()
        }
        .onChange(of: showImmsersiveSpace) { _, newValue in
            Task {
                if newValue {
                    await openImmersiveSpace(id: "ImmersiveSpace")
                } else {
                    await dismissImmersiveSpace()
                }
            }
        }
    }
}
#Preview {
    ContentView()
}

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

接下来就编写入口文件:

import SwiftUI
@main
struct visionOSDemoApp: App {
    var body: some Scene {
        WindowGroup() {
            ContentView()
        }
        ImmersiveSpace(id: "ImmersiveSpace") {
            ImmersiveView()
        }
    }
}

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

接下来是核心文件ViewModel.swift

import SwiftUI
import RealityKit
@MainActor class ViewModel: ObservableObject {
    private var contentEntity = Entity()
    func setupContentEntity() -> Entity {
        return contentEntity
    }
    func addCube() {
        guard
            let texture1 = try? TextureResource.load(named: "Number_1"),
            let texture2 = try? TextureResource.load(named: "Number_2"),
            let texture3 = try? TextureResource.load(named: "Number_3"),
            let texture4 = try? TextureResource.load(named: "Number_4"),
            let texture5 = try? TextureResource.load(named: "Number_5"),
            let texture6 = try? TextureResource.load(named: "Number_6")
        else {
            fatalError("Unable to load texture.")
        }
        let entity = Entity()
        var material1 = SimpleMaterial()
        var material2 = SimpleMaterial()
        var material3 = SimpleMaterial()
        var material4 = SimpleMaterial()
        var material5 = SimpleMaterial()
        var material6 = SimpleMaterial()
        material1.color = .init(texture: .init(texture1))
        material2.color = .init(texture: .init(texture2))
        material3.color = .init(texture: .init(texture3))
        material4.color = .init(texture: .init(texture4))
        material5.color = .init(texture: .init(texture5))
        material6.color = .init(texture: .init(texture6))
        entity.components.set(ModelComponent(
            mesh: .generateBox(width: 0.5, height: 0.5, depth: 0.5, splitFaces: true),
            materials: [material1, material2, material3, material4, material5, material6]
        ))
        entity.position = SIMD3(x: 0, y: 1, z: -2)
        contentEntity.addChild(entity)
    }
}

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

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

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

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

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

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

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

import SwiftUI
import RealityKit
struct ImmersiveView: View {
    @StateObject var model = ViewModel()
    private var contentEntity = Entity()
    var body: some View {
        RealityView { content in
            content.add(model.setupContentEntity())
            model.addCube()
        }
    }
}

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

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

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

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

示例代码:GitHub仓库

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

相关文章
|
6月前
|
Ubuntu 安全 数据安全/隐私保护
在Docker容器中部署GitLab服务器的步骤(面向Ubuntu 16.04)
现在,你已经成功地在Docker上部署了GitLab。这就是我们在星际中的壮举,轻松如同土豆一样简单!星际旅行结束,靠岸,打开舱门,迎接全新的代码时代。Prepare to code, astronaut!
481 12
|
Kubernetes API 调度
Kubernetes 架构解析:理解其核心组件
【8月更文第29天】Kubernetes(简称 K8s)是一个开源的容器编排系统,用于自动化部署、扩展和管理容器化应用。它提供了一个可移植、可扩展的环境来运行分布式系统。本文将深入探讨 Kubernetes 的架构设计,包括其核心组件如何协同工作以实现这些功能。
976 2
|
API 开发工具 C#
一套基于 .NET Core 开发的支付SDK集 - paylink
一套基于 .NET Core 开发的支付SDK集 - paylink
264 1
|
传感器 机器人 数据处理
ROS 编程入门的介绍
2.1 创建 ROS 功能包 ROS(Robot Operating System)是一种开源的机器人软件框架,广泛用于机器人开发中。通过使用 ROS,开发者可以轻松创建和管理机器人应用程序。在本节中,我们将介绍如何创建一个 ROS 功能包并实现一些基本功能。 2.1.1 使用 ROS 主题 ROS 主题(Topic)是一种发布/订阅机制,允许节点之间进行通信。每个节点可以发布主题消息或订阅主题消息来获取数据。以下是如何使用 ROS 主题的步骤: 创建功能包 首先,我们需要创建一个新的 ROS 功能包。在终端中运行以下命令: catkin_create_pkg de_ws my_r
391 0
|
机器学习/深度学习 人工智能 自然语言处理
AIGC技术革新:智能创造如何重塑艺术与设计行业
AIGC技术,人工智能生成内容,正引领艺术与设计行业的变革。借助深度学习和自然语言处理等技术,AIGC能自动生成文本、图像等内容,丰富创作手段并提供创新机会。在艺术领域,它模拟各种风格作品,助力高效创作;在设计领域,它根据用户需求生成设计方案,提升个性化选择。AIGC打破了传统界限,提高了创作效率,并满足了用户的个性化需求。未来,随着技术进步和应用场景拓展,AIGC将在虚拟现实等领域的结合中,为艺术与设计带来更沉浸式、交互式的体验,重塑行业未来。【6月更文挑战第4天】
1335 1
|
芯片 内存技术
PCB相关知识-焊盘Pad
焊盘就是元器件封装中的引脚,在实际应用中使用焊锡将电阻、电容、电感、芯片等元器件的引脚和焊盘Pad连接在一起(电气连接)。焊盘有多种形式,按照不同封装分为:通孔焊盘(直插元件)和表贴焊盘(表贴元件);按照形状分为:规则焊盘和异形焊盘,这个就需要根据具体的芯片封装来进行设计了。不管怎么分类,一个焊盘都是由多个部分组成的。在设计焊盘的时候,有许多人还是搞不清楚常规焊盘Regrlar Pad、热焊盘Thermal Pad、隔离焊盘Anti Pad之间的区别,以及什么时候使用。
480 0
|
Docker Python Windows
Docker selenium 自动化 - 使用python操作docker,python运行、启用、停用和查询容器实例演示
Docker selenium 自动化 - 使用python操作docker,python运行、启用、停用和查询容器实例演示
1517 0
Docker selenium 自动化 - 使用python操作docker,python运行、启用、停用和查询容器实例演示
|
测试技术 数据安全/隐私保护 Java
基于SpringBoot+Vue+uniapp的超市商品管理系统的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue+uniapp的超市商品管理系统的详细设计和实现(源码+lw+部署文档+讲解等)
217 0
|
API Swift iOS开发
【Swift开发专栏】Swift中的游戏开发入门
【4月更文挑战第30天】本文介绍了使用Swift进行移动游戏开发的基础知识,包括Apple的开发平台(iOS, macOS)、工具(Xcode)、2D/3D游戏框架(SpriteKit, SceneKit)以及Metal图形API。Swift游戏开发涉及游戏循环、UI设计、逻辑、图形音效和网络编程。通过实例教程展示如何在Xcode中创建2D游戏,从创建项目到实现用户交互、音效和测试。掌握这些基础知识,开发者可快速入门并逐步进阶到更复杂的游戏中。
652 1
|
存储 缓存 关系型数据库
Binlog vs. Redo Log:数据库日志的较劲【基础】
Binlog vs. Redo Log:数据库日志的较劲【基础】
604 0