[SWIFT] Affichage du curseur lors du placement d'objets avec ARKit + SceneKit

À propos du curseur à afficher lors du placement d'un objet arbitraire dans AR.

Chose que tu veux faire

Ceci est une application de mesure standard. keisoku_app.png

Transformer le curseur

Effectuez un test de succès sur les coordonnées centrales de l'écran et définissez-les en fonction du worldTransform acquis. Puisque l'emplacement atteint par hitTest (_: types:) de ʻARSCNView peut être obtenu à partir de worldTransform de ʻARHitTestResult, il est utilisé comme emplacement et orientation du curseur. Ici, lorsque le curseur est réglé sur SCNPlane, si l'emplacement / l'orientation du curseur = l'emplacement / l'orientation de la frappe, il interfère avec la géométrie du plan et scintille, donc le vecteur ascendant de l'avionworldTransform.columns.1 Utilisez pour ajuster la position du curseur.

//0 dans la direction (HAUT) face au plan.Placez le curseur sur une position décalée de 01m
cursorTransform.columns.3 += worldTransform.columns.1 * 0.01
self.cursorNode.simdTransform = cursorTransform

Notez que la mise à l'échelle est réinitialisée si le SCNNode est mis à l'échelle ( scale ≠ (1.0,1.0,1.0)) car la transformation est définie. Dans ce cas, définissez la valeur obtenue en multipliant worldTransform.columns.0 ~ 2 par le rapport de mise à l'échelle sur cursorTransform.columns.0 ~ 2.

C'est fait

Il a une forme pyramidale de sorte que la direction ascendante du curseur peut être facilement comprise. demo.gif

Code source

ViewController.swift


import ARKit
import SceneKit

class ViewController: UIViewController, ARSCNViewDelegate {

    @IBOutlet weak var scnView: ARSCNView!
    
    private let device = MTLCreateSystemDefaultDevice()!
    private let cursorNode = SCNNode()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //Session AR démarrée
        self.scnView.delegate = self
        let configuration = ARWorldTrackingConfiguration()
        configuration.planeDetection = [.horizontal, .vertical]
        self.scnView.session.run(configuration, options: [.removeExistingAnchors, .resetTracking])
        //Préparation du nœud de curseur
        let pyramid = SCNPyramid(width: 0.1, height: 0.03, length: 0.1)
        pyramid.firstMaterial!.diffuse.contents = UIColor.yellow
        self.cursorNode.geometry = pyramid
        self.scnView.scene.rootNode.addChildNode(self.cursorNode)
        self.cursorNode.isHidden = true
    }
    //
    //Ancre ajoutée
    //
    func renderer(_: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
        guard let planeAnchor = anchor as? ARPlaneAnchor else { return }

        //Ajout d'un nœud de géométrie plane
        guard let geometory = ARSCNPlaneGeometry(device: self.device) else { return }
        geometory.update(from: planeAnchor.geometry)
        let material = SCNMaterial()
        material.lightingModel = .physicallyBased
        material.diffuse.contents = UIColor.red.withAlphaComponent(0.7)
        geometory.materials = [material]
        let planeNode = SCNNode(geometry: geometory)
        DispatchQueue.main.async {
            node.addChildNode(planeNode)
        }
    }
    //
    //Ancre mise à jour
    //
    func renderer(_: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
        guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
        
        DispatchQueue.main.async {
            for childNode in node.childNodes {
                //Mettre à jour la géométrie du plan
                guard let planeGeometry = childNode.geometry as? ARSCNPlaneGeometry else { continue }
                planeGeometry.update(from: planeAnchor.geometry)
                break
            }
        }
    }
    //
    //Appelé image par image
    //
    func renderer(_ renderer: SCNSceneRenderer, updateAtTime _: TimeInterval) {
        DispatchQueue.main.async {
            //Hit test au centre de l'écran
            let bounds = self.scnView.bounds
            let screenCenter =  CGPoint(x: bounds.midX, y: bounds.midY)
            let results = self.scnView.hitTest(screenCenter, types: [.existingPlaneUsingGeometry])
            guard let existingPlaneUsingGeometryResult = results.first(where: { $0.type == .existingPlaneUsingGeometry }),
                  let _ = existingPlaneUsingGeometryResult.anchor as? ARPlaneAnchor else {
                //Masquer le curseur
                self.cursorNode.isHidden = true
                return
            }
            
            //Publier la transformation de l'emplacement du hit à la transformation du curseur
            let worldTransform = existingPlaneUsingGeometryResult.worldTransform
            var cursorTransform = worldTransform
            //0 dans la direction (HAUT) face au plan.Placez le curseur sur une position décalée de 01m
            cursorTransform.columns.3 += worldTransform.columns.1 * 0.01
            self.cursorNode.simdTransform = cursorTransform
            
            self.cursorNode.isHidden = false
        }
    }
}

Recommended Posts

Affichage du curseur lors du placement d'objets avec ARKit + SceneKit
Effondrement du sol avec ARKit + SceneKit
Camouflage optique avec ARKit + SceneKit + Metal ①
Navigation Web avec ARKit + SceneKit + Metal
Camouflage optique avec ARKit + SceneKit + Metal ②
Changez l'écran d'affichage lorsque vous passez la souris sur un onglet avec jQuery