使用多个AR图像锚点显示动态3D叠加层

时间:2018-08-14 06:05:48

标签: ios swift arkit image-recognition arimageanchor

我正在一个项目中,我们必须检测一定数量的自定义QR码(如ARImageAnchors),然后使用这些锚点的位置动态显示3D叠加层。确切地说,我们计划在将要放置在人体模型上的锚点上动态显示人体解剖3D模型。例如,我们要放置QR码的人体模型小于或大于3D模型的默认大小,我们希望它能够根据图像之间的距离进行调整。以下是我正在考虑的示例代码(源:https://www.appcoda.com/arkit-image-recognition/)。

import UIKit
import ARKit

class ViewController: UIViewController {

@IBOutlet weak var sceneView: ARSCNView!
@IBOutlet weak var label: UILabel!

let fadeDuration: TimeInterval = 0.3
let rotateDuration: TimeInterval = 3
let waitDuration: TimeInterval = 0.5

lazy var fadeAndSpinAction: SCNAction = {
    return .sequence([
        .fadeIn(duration: fadeDuration),
        .rotateBy(x: 0, y: 0, z: CGFloat.pi * 360 / 180, duration: rotateDuration),
        .wait(duration: waitDuration),
        .fadeOut(duration: fadeDuration)
        ])
}()

lazy var fadeAction: SCNAction = {
    return .sequence([
        .fadeOpacity(by: 0.8, duration: fadeDuration),
        .wait(duration: waitDuration),
        .fadeOut(duration: fadeDuration)
        ])
}()

lazy var treeNode: SCNNode = {
    guard let scene = SCNScene(named: "tree.scn"),
        let node = scene.rootNode.childNode(withName: "tree", recursively: false) else { return SCNNode() }
    let scaleFactor = 0.005
    node.scale = SCNVector3(scaleFactor, scaleFactor, scaleFactor)
    node.eulerAngles.x = -.pi / 2
    return node
}()

lazy var bookNode: SCNNode = {
    guard let scene = SCNScene(named: "book.scn"),
        let node = scene.rootNode.childNode(withName: "book", recursively: false) else { return SCNNode() }
    let scaleFactor  = 0.1
    node.scale = SCNVector3(scaleFactor, scaleFactor, scaleFactor)
    return node
}()

lazy var mountainNode: SCNNode = {
    guard let scene = SCNScene(named: "mountain.scn"),
        let node = scene.rootNode.childNode(withName: "mountain", recursively: false) else { return SCNNode() }
    let scaleFactor  = 0.25
    node.scale = SCNVector3(scaleFactor, scaleFactor, scaleFactor)
    node.eulerAngles.x += -.pi / 2
    return node
}()

override func viewDidLoad() {
    super.viewDidLoad()
    sceneView.delegate = self
    configureLighting()
}

func configureLighting() {
    sceneView.autoenablesDefaultLighting = true
    sceneView.automaticallyUpdatesLighting = true
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    resetTrackingConfiguration()
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    sceneView.session.pause()
}

@IBAction func resetButtonDidTouch(_ sender: UIBarButtonItem) {
    resetTrackingConfiguration()
}

func resetTrackingConfiguration() {
    guard let referenceImages = ARReferenceImage.referenceImages(inGroupNamed: "AR Resources", bundle: nil) else { return }
    let configuration = ARWorldTrackingConfiguration()
    configuration.detectionImages = referenceImages
    let options: ARSession.RunOptions = [.resetTracking, .removeExistingAnchors]
    sceneView.session.run(configuration, options: options)
    label.text = "Move camera around to detect images"
}
}

extension ViewController: ARSCNViewDelegate {

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
    DispatchQueue.main.async {
        guard let imageAnchor = anchor as? ARImageAnchor,
            let imageName = imageAnchor.referenceImage.name else { return }

        // TODO: Comment out code
        //            let planeNode = self.getPlaneNode(withReferenceImage: imageAnchor.referenceImage)
        //            planeNode.opacity = 0.0
        //            planeNode.eulerAngles.x = -.pi / 2
        //            planeNode.runAction(self.fadeAction)
        //            node.addChildNode(planeNode)

        // TODO: Overlay 3D Object
        let overlayNode = self.getNode(withImageName: imageName)
        overlayNode.opacity = 0
        overlayNode.position.y = 0.2
        overlayNode.runAction(self.fadeAndSpinAction)
        node.addChildNode(overlayNode)

        self.label.text = "Image detected: \"\(imageName)\""
    }
}

func getPlaneNode(withReferenceImage image: ARReferenceImage) -> SCNNode {
    let plane = SCNPlane(width: image.physicalSize.width,
                         height: image.physicalSize.height)
    let node = SCNNode(geometry: plane)
    return node
}

func getNode(withImageName name: String) -> SCNNode {
    var node = SCNNode()
    switch name {
    case "Book":
        node = bookNode
    case "Snow Mountain":
        node = mountainNode
    case "Trees In the Dark":
        node = treeNode
    default:
        break
    }
    return node
}

}

我知道3D叠加层显示在上面的 renderer 函数中,但它仅显示在检测到的单个图像锚点的顶部。现在我的问题是,是否可以引用多个ARImage锚以动态显示单个 3D模型?

作为ARKit和Swift的新手,我不确定该如何解决。我希望有人对如何解决这个问题有个想法,并指出正确的方向。任何帮助将不胜感激!

谢谢!

0 个答案:

没有答案