0
点赞
收藏
分享

微信扫一扫

【iOS ARKit】3D文字

辰鑫chenxin 03-26 07:00 阅读 4
iosAR

   首先,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文字的方法达到更方便使用的目的。

举报

相关推荐

0 条评论