IOS开发之AR问题汇总

问题一:怎么使用SwiftUI实现AR界面

目前IOS开发AR有两个框架,一个是ARKit的ARSCNView,另一个是RealityKit的ARView

后者比前者更新功能更强大一些,也更推荐使用

现在我们普遍使用的语言是SwiftUI,而上面两个框架继承的都是UIKit,是旧的框架

因此,我们实现的时候需要使用其他的类来桥接这两个框架

UIViewRepresentable 协议类

通过该协议就可以让我们轻松地在 SwiftUI 界面上使用 UIView

这个协议有两个必须提供的方法:makeUIViewupdateUIView

下面我使用ARView举例

struct TestARViewContainer: UIViewRepresentable {
    func makeUIView(context: Context) -> ARView {
        return ARView()
    }

    func updateUIView(_ uiView: ARView, context: Context) {
    }
}
  • makeUIView 方法创建了一个要表示的 UIView, 在其生命周期里只会调用一次
  • updateUIView 方法会在 UIView 的状态发生变化时被调用,所以在其生命周期内会被调用多次(即使这是一个空实现也会被调用多次)。

一般我们只会这用到makeUIView

问题二:AR模型有什么注意的点吗

有!AR模型仅支持usdz格式的,这是苹果独有的模型格式

这个格式我自认为很鸡肋,是因为模型里只能展示一个动画,也就是说,即使你的模型里有多个动画,也只能展示出一个,这个问题官方一直都没有管!!!

如果你要实现一个模型,在不同条件下做不同的动作,唯一解决的办法就是:

把每一个动作都单独提出来做成一个模型,当你需要切换动作的时候,直接切换包含对应动作的模型

模型动作分离的教程,我之前也有写过,小白的话可以看一下,很简单就能完成✅

但是代码部分的实现很头痛,因为模型加载时需要时间的,如果你切换动作的时候,先加载模型,再播放动画,会有明显的卡壳的感觉

这里我提供一个思路,就是使用全局静态,一次性异步加载所有我们需要的包含动作的模型

这里给大家代码示范一下

首先是静态类

//
//  TestModel.swift
//  ARDemo
//
//  Created by hp on 2024/9/2.
//

import Foundation
import RealityKit
import Combine
import SwiftUI

enum ModelCategory: CaseIterable{
    case rollover
    case play
    case shake
    case sit
    case stand
    
    var label: String{
        get{
            switch self{
            case .rollover:
                return "Rollover"
            case .play:
                return "Play"
            case .shake:
                return "Shake"
            case .sit:
                return "Sit"
            case .stand:
                return "Stand"
            }
        }
    }
}

class TestModel{
    var name: String
    var category: ModelCategory
    var modelEntity: ModelEntity?
    var scaleCompensation: Float
    
    private var cancellable: AnyCancellable?
    
    init(name: String, category: ModelCategory, scaleCompensation: Float = 1.0) {
        self.name = name
        self.category = category
        self.scaleCompensation = scaleCompensation
    }
    
    func asyncLoadModelEntity(){
        let filename = self.name + ".usdz"
        
        self.cancellable = ModelEntity.loadModelAsync(named: filename)
            .sink(receiveCompletion: { loadCompletion in
                switch loadCompletion{
                case .finished:
                    break
                case.failure(let error): print("Unable ton load modelEntity for \(filename).Error: \(error.localizedDescription)")
                }
            }, receiveValue: { modelEntity in
                self.modelEntity = modelEntity
                self.modelEntity?.scale /= 10
                self.modelEntity?.scale += self.scaleCompensation
                print("modelEntity for \(self.name) has been loaded")
                
            })
    }
}

//使用单例模式,异步初始化所有狗模型
struct TestModels{
    static let shared = TestModels()
    var all: [TestModel] = []
    
    private init(){
        print("TestModels init")
        let rollover = TestModel(name: "rollover", category: .rollover, scaleCompensation: 0.1/100)
        let play = TestModel(name: "play", category: .play, scaleCompensation: 0.1/100)
        let shake = TestModel(name: "shake", category: .shake, scaleCompensation: 0.1/100)
        let sit = TestModel(name: "sit", category: .sit, scaleCompensation: 0.1/100)
        let stand = TestModel(name: "stand", category: .sit, scaleCompensation: 0.1/100)
        rollover.asyncLoadModelEntity()
        play.asyncLoadModelEntity()
        shake.asyncLoadModelEntity()
        sit.asyncLoadModelEntity()
        stand.asyncLoadModelEntity()
        self.all += [rollover,play,shake,sit,stand]
    }
    
    func get(category: ModelCategory) ->[TestModel] {
        return all.filter({
            $0.category == category
        })
    }
}

使用单例模式后,在第一次创建AR模型的时候,会自动异步加载所有的模型,方便后续切换

比如我第一次加载一个坐着的狗模型,实际上会把其他所有动作的狗模型都加载了

var items = TestModels.shared.get(category: .sit)

那么接下来怎么替换呢

这里我给个样例

func replaceModel(action: ModelCategory){
    guard let arView = arView else{return}
    //arView.scene.anchors.removeAll()
    if let entity = arView.scene.findEntity(named: "dog"){
        //保存位置
        var position = entity.position
        //移除模型,后面那个数字是需要自己试的
        arView.scene.anchors.remove(at: 2)
        print("Remove Success!")
                 
        //新建有动画的模型
        var items = TestModels.shared.get(category: action)
        let model = items[0]
        if let modelEntity = model.modelEntity{
        let clonedEntity = modelEntity.clone(recursive: true)
        //添加旋转角等特性
        clonedEntity.generateCollisionShapes(recursive: true)
        arView.installGestures([.translation, .rotation], for: clonedEntity)
                     
        //创建锚点
        let anchorEntity = AnchorEntity(.plane(.horizontal, classification: .any, minimumBounds: SIMD2(repeating: 0)))
        clonedEntity.name = "dog"
        anchorEntity.addChild(clonedEntity)
        arView.scene.addAnchor(anchorEntity)
        print("Create success!")
                     
        //播放动画
        clonedEntity.playAnimation((clonedEntity.availableAnimations[0].repeat()), transitionDuration: 0.5, startsPaused: false)
        let animationDuration = clonedEntity.availableAnimations[0].definition.duration
        let timer = Timer.scheduledTimer(withTimeInterval: animationDuration, repeats: false, block: { _ in
             print("Animation over!")
             })
        }
    }
}

实际上位置变量没有用到,默认如果删除模型后新加的位置还是原来的位置

这是我进过测试总结出来的

问题三:怎么使用Unity作AR开发呢

做过AR开发的,或多或少都知道Unity的ARFundation可以进行AR开发

我也使用过,但是开发的是安卓版本的!

IOS的版本做好之后,需要打包成Xcode可以调用的库文件

但这时候会有个问题,就是所有的AR部分你必须在Unity中全部实现,因为Xcode调用Unity的库文件,是不能传输信息的

所以我最后没有选择用Unity进行IOS端的AR开发!

好了这就是本次博客的全部内容了,欢迎大家与我一起分享和讨论,我们一起学习,一起进步!!!

非常抱歉,我之前提供的代码存在错误。在 PyTorch 中,并没有直接提供离散余弦变换(DCT)的函数。对于 DCT 的实现,你可以使用 `torch.rfft` 函数结合 DCT 系数矩阵来进行计算。 下面是一个修正后的示例代码: ```python import torch import torch.nn as nn import torch.optim as optim # 定义离散余弦变换(DCT)系数矩阵 dct_matrix = torch.zeros(256, 256) for i in range(256): for j in range(256): dct_matrix[i, j] = torch.cos((2 * i + 1) * j * 3.14159 / (2 * 256)) # 定义 OMP 算法 def omp(A, y, k): m, n = A.shape x = torch.zeros(n, 1) residual = y.clone() support = [] for _ in range(k): projections = torch.abs(A.t().matmul(residual)) index = torch.argmax(projections) support.append(index) AtA_inv = torch.linalg.inv(A[:, support].t().matmul(A[:, support])) x_new = AtA_inv.matmul(A[:, support].t()).matmul(y) residual = y - A[:, support].matmul(x_new) x[support] = x_new return x # 加载原始图像 image = torch.randn(256, 256) # 压缩感知成像 measurement_matrix = torch.fft.fft(torch.eye(256), dim=0).real compressed = measurement_matrix.matmul(image.flatten().unsqueeze(1)) # 使用 OMP 进行重构 reconstructed = omp(dct_matrix, compressed, k=100) # 计算重构误差 mse = nn.MSELoss() reconstruction_error = mse(image, reconstructed.reshape(image.shape)) print("重构误差:", reconstruction_error.item()) ``` 在这个示例中,我们手动定义了 DCT 系数矩阵 `dct_matrix`,然后使用 `torch.fft.fft` 函数计算测量矩阵,并进行实部提取。接下来的步骤与之前的示例相同。 请注意,这只是一个示例,用于演示如何使用自定义的 DCT 系数矩阵进行压缩感知成像。在实际应用中,你可能需要根据具体的需求进行调整和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海绵不是宝宝817

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值