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开发学习笔记

目录
打赏
0
0
0
0
75
分享
相关文章
老程序员分享:OpenGL学习进程(10)第七课:四边形绘制与动画基础
老程序员分享:OpenGL学习进程(10)第七课:四边形绘制与动画基础
|
10月前
|
Android开发之OpenGL绘制三维图形的流程
即将连载的系列文章将探索Android上的OpenGL开发,这是一种用于创建3D图形和动画的技术。OpenGL是跨平台的图形库,Android已集成其API。文章以2D绘图为例,解释了OpenGL的3个核心元素:GLSurfaceView(对应View)、GLSurfaceView.Renderer(类似Canvas)和GL10(类似Paint)。通过将这些结合,Android能实现3D图形渲染。文章介绍了Renderer接口的三个方法,分别对应2D绘图的构造、测量布局和绘制过程。示例代码展示了如何在布局中添加GLSurfaceView并注册渲染器。
261 1
Android开发之OpenGL绘制三维图形的流程
visionOS空间计算实战开发教程Day 2 使用RealityKit显示3D素材
我们在Day1中学习了如何创建一个visionOS应用,但在第一个Demo应用中我们的界面内容还是2D的,看起来和其它应用并没有什么区别。接下来我们先学习如何展示3D素材,苹果为方便开发人员,推出了RealityKit,接下来看如何使用。
138 0
从0开发游戏引擎之在3D空间中渲染出三维几何体
这个类里面会使用第9章里的三维体数据来调用OpenGL的接口绘制出来对应的形状。几何体绘制类主要是调试使用的,比如想要更直观的看到一个对象身上的碰撞框。绘制的形状非常多,大家直接看代码吧。
从0开发游戏引擎之三维几何体数据类
Shape类只是单纯的形状数据,并不能用具真正的绘制,真正的绘制工作是Gizmo类去做的。该类只是作为Gizmo的一个成员去使用的。Shape的函数只是提供了加工数据的,然后把加工后的数据存下来。不多哔哔了,直接贴代码。原理有空了再详细写。
案例分享:Qt+OSG三维点云引擎(支持原点,缩放,单独轴或者组合多轴拽拖旋转,支持导入点云文件)
案例分享:Qt+OSG三维点云引擎(支持原点,缩放,单独轴或者组合多轴拽拖旋转,支持导入点云文件)
简简单单修改游戏对象的材质颜色,一起来看看(Unity3D)
前段时间比较忙,好久没更新博客了,感觉技术都下降了,还是要坚持输出呀。 孔子曰:"学而不思则罔,思而不学则殆",不能光学习,还要学会思考,要能用起来。 将自己学到的东西记录下来,这样会让学习更加有效。
ThingJS是如何编辑3D空间物体的?
人所在的三维空间很复杂,情景也会随着人与任意对象之间的任务而发生变化
ThingJS是如何编辑3D空间物体的?
带你读《Unity游戏开发(原书第3版)》之三:模型、材质和纹理
本书主要介绍Unity2018的使用和游戏开发流程中涉及的各种知识。每一章的结构特别清晰,先综述该章要介绍的内容,然后一步步深入讲解,中间穿插着很多动手做的实践操作,可以让读者加深对某个概念、方法的理解,每章的最后还有一个小测验和一个稍微大一点的实践练习,用于巩固该章的学习内容。