首先,3D场景中渲染的任何虚拟元素都必须具有网格(顶点及顶点间的拓扑关系),没有网格的元素无法利用GPU 进行渲染,因此,在3D 场景申渲染 3D文字时,文字也必须具有网格。在计算机系统中,文字以平面点阵的形式存储和表示,所以进行3D文字渲染,需要将平面点阵转换为3D网格。
在 RealityKit 中,开发人员可以程序化地生成立方体、球体、圆柱体等3D虚拟对象,这个过程其实就是利用算法生成立方体、球体、圆柱体的网格信息、法线信息、UV坐标信息的过程,有了这些基础信息,CPU与 GPU 就知道如何将虚拟对象渲染出来。
RealityKit 也提供了根据指定文字自动生成文字网格、法线信息、UV坐标信息的方法 generateText(),该方法返回 MeshResource 类型对象,利用这个对象就可以对文字进行3D 渲染。在 RealityKit 中,生成3D文字的典型代码如代码如下所示。
//
// Text3DView.swift
// ARKitDeamo
//
// Created by zhaoquan du on 2024/3/21.
//
import SwiftUI
import ARKit
import RealityKit
import Combine
struct Text3DView: View {
@State var change: String = "中文汉字"
var body: some View {
Text3DViewContainer(change: change)
.overlay(
VStack{
Spacer()
TextField( LocalizedStringKey(""), text: $change)
.foregroundColor(.black)
.background(Color.white)
.frame(width:300,height:50)
.cornerRadius(5)
.opacity(0.6)
.offset(y:-330)
.padding(.bottom, 300)
}
).navigationTitle("3D文字").edgesIgnoringSafeArea(.all)
}
}
struct Text3DViewContainer:UIViewRepresentable {
var change:String = ""
func makeUIView(context: Context) -> some ARView {
let arView = ARView(frame: .zero)
context.coordinator.arView = arView
let config = ARWorldTrackingConfiguration()
config.planeDetection = .horizontal
context.coordinator.createPlane()
arView.session.run(config)
return arView
}
func updateUIView(_ uiView: UIViewType, context: Context) {
if !change.isEmpty {
context.coordinator.chengeText(text: change)
}
}
func makeCoordinator() -> Coordinator {
Coordinator()
}
class Coordinator: NSObject {
var arView: ARView!
var text: String = ""
var textEntity: ModelEntity!
func createPlane() {
let planeAnchor = AnchorEntity(plane: .horizontal)
let textr = MeshResource.generateText("中文汉字",
extrusionDepth: 0.05,
font: .systemFont(ofSize: 15),
containerFrame: .zero,
alignment: .left,
lineBreakMode: .byWordWrapping)
let textMetiral = SimpleMaterial(color: .red, isMetallic: true)
textEntity = ModelEntity(mesh: textr, materials: [textMetiral])
textEntity.generateCollisionShapes(recursive: false)
planeAnchor.addChild(textEntity)
arView.scene.addAnchor(planeAnchor)
arView.installGestures(.all, for: textEntity)
}
func chengeText(text: String) {
let planeAnchor = AnchorEntity(plane: .horizontal)
let textr = MeshResource.generateText(text,
extrusionDepth: 0.05,
font: .systemFont(ofSize: 2),
containerFrame: .zero,
alignment: .left,
lineBreakMode: .byWordWrapping)
let textMetiral = SimpleMaterial(color: .red, isMetallic: true)
textEntity.removeFromParent()
textEntity = ModelEntity(mesh: textr, materials: [textMetiral])
textEntity.generateCollisionShapes(recursive: false)
planeAnchor.addChild(textEntity)
arView.scene.addAnchor(planeAnchor)
arView.installGestures(.all, for: textEntity)
}
}
}
#Preview {
Text3DView()
}
从代码可以看到,生成3D文字的过程与生成其他程序化虚拟模型对象的过程完全一致,唯一区别是生成 3D 文字网格的方法要求设置的参数更多,generateText()方法原型
static func generateText (_ string: String, extrusionDepth: Float = 0. 25, font: MeshResource. Font = .systemPont(ofSize: MeshResource. Font. systemFontSize), containerFrame: CGRect = CGRect. zero, alignment: CTTextAlignment =. left, lineBreakMode: CTLineBreakMode = byTruncatingTail) - > MeshResource
generateText()方法参数众多,但实际除了 string 其余参数都可以使用默认值,各参数的意义如下表所示。
表11-1 生成3D文字网格的参数属性
参数名 | 描述 |
string | 需要3D渲染的文字,使用内置的systemFont 可以渲染中文汉字与英文字符,如果使用其他字体渲染中文汉字需要确保字体支持 |
extrusionDepth | 渲染的文字厚度,即在Z轴上的长度,以米为单位 |
font | 渲染所用字体,渲染中文汉字需要字体支持,使用该属性可以指定字体大小。默认使用系统字体 |
containerFrame | 该属性指定文字所占空间尺寸,类似于 Word文字排版软件中的文本框指定文字所占尺寸,当指定该值时,如果文字渲染超出该尺寸则会以 lineBreakMode 属性指定的方式截断。默认为(0,0),会以最合适的大小包裹所有文字 |
alignment | 文字在 containerFrame 中的对齐方式,可以为 center(居中对齐)、justified(分散对齐)、left(左对齐)、natural(两端对齐)、right(右对齐)之一,该属性会影响缩放、旋转3D文字时的定位点 |
lineBreakMode | 文字超出 containerFrame 范围时的截断方式,可以 byWordWrapping(以单词/汉字为单位显示,超出部分不显示)、byCharWrapping(以字符/汉字单位显示,超出部分不显示)、byClipping(剪切与containerFrame 尺寸一致的内容长度,后半部分被截断)、byTruncatingHead(前面文字被截断,用省略号显示)、byTruncatingTail(后面文字被截断,用省略号显示)、byTruncatingMiddle(两端文字保留,中间文字被省略,用省略号显示)之一 |
generateText()方法生成的文字 3D网格可以与其他程序化虚拟模型对象一样被赋子材质,包括纹理,也可以使用 ARAnchor 将其固定到场景中。
在 RealityKit 中生成的文字 3D网格不可修改,因此,无法通过网格修改的方式更新谊染的3D文字,果需要更新已生成的3D文字,则只能重新生成新的文字3D网格。
上述代码我们直接使用 changeText()方法重新生成新的文字3D网格,然后重新生成 textEntity 实体更新渲染的3D文字。在实际开发中,也可以通过扩展(extension) Entity 或者 ModelEntity 类,添加更新 3D文字的方法达到更方便使用的目的。