Comment se lancer dans la réalité augmentée dans Swift, en toute simplicité

Si vous regardez autour de vous, c'est l'âge d'or de la technologie. Chaque discours ajoute quelque chose de nouveau à la pile de technologies existante. C’est passionnant de voir comment ces technologies émergentes ont amélioré les limites de notre imagination. En tant que développeur, nous devons être fiers d’être les premiers utilisateurs de ces technologies.

Mais chaque nouvelle technologie nécessite une courbe d'apprentissage assez abrupte. Vous ne pouvez pas regarder un discours ou une vidéo sur Youtube et commencer à développer une application. La bonne nouvelle est qu’avec Sw dans AR, il est remarquablement facile de travailler avec des applications AR de base. Apple a fait le gros du travail pour vous. Suivez le mouvement et vous verrez comme cela peut être facile.

Creusons…

Dans ce didacticiel, nous allons apprendre les outils et techniques nécessaires à la RA dans Swift, ce qui nous permettra de créer une application qui décore votre sol avec des carreaux de sol et des textures en bois. L'application finie ressemblera à ceci:

Commençons par créer une application à vue unique dans Xcode et appelez-la Home Decor.

Ajout d'autorisations de caméra

Maintenant, la première chose à faire est de naviguer dans le fichier info.plist et de permettre l’utilisation de la caméra. La capacité de l'appareil photo est la première chose dont vous avez besoin pour une application AR. Recherchez la clé Description de l'utilisation de l'appareil photo, comme dans l'image ci-dessous, et transmettez-lui un message approprié. Ce message apparaîtra au tout premier lancement de l'application tout en demandant les autorisations de l'appareil photo à l'utilisateur.

Ajout de fonctionnalités ARKit à l'application

Allez à Main.storyboard. Glissez-déposez une vue ARKit SceneKit sur le ViewController et épinglez-la sur les bords du ViewController.

Créez un IBOutlet pour la classe ViewController et nommez-le sceneView. Dès que vous le faites, une erreur indiquant ARSCNView non déclaré apparaîtra, car notre contrôleur de vue ne reconnaît rien de type ARSCNView. Pour résoudre ce problème et utiliser d'autres fonctionnalités d'ARKit, nous devons importer ARKit dans le contrôleur de vue.

Passez maintenant du storyboard au fichier view controller.swift. Déclarez une propriété de type ARWorldTrackingConfiguration avant la méthode viewDidLoad () et nommez-la config. Et notre contrôleur de vue ressemblera à ceci (j'ai supprimé la méthode didReceiveMemoryWarning):

importer UIKit
importer ARKit
class ViewController: UIViewController {
@IBOutlet faible var sceneView: ARSCNView!
let config = ARWorldTrackingConfiguration ()
remplacer func viewDidLoad () {
super.viewDidLoad ()
}

Autoriser le débogage

Cette variable de configuration déterminera les configurations de la session de la scène. Nous verrons cela plus tard dans la section. Maintenant, dans la méthode viewDidLoad après super.viewDidLoad (), ajoutez ce qui suit:

sceneView.debugOptions = [ARSCNDebugOptions.showFeaturePoints, ARSCNDebugOptions.showWorldOrigin]

Nous activons ici les options de débogage pour notre sceneView, qui n’est autre que la vue caméra avec les capacités du cadre AR. ARSCNDebugOptions.showWorldOrigin affichera l’origine du monde à l’écran. Cela nous aidera à trouver le point de référence de tous les autres postes. ARSCNDebugOptions.showFeaturePoints affichera sur l'écran tous les points que la caméra AR a reconnus dans l'environnement.

Maintenant, pour démarrer la session AR, nous devons exécuter une session dans notre sceneView avec les configurations mentionnées dans la variable config. Juste en dessous de la ligne sceneView.debugOptions, écrivez:

sceneView.session.run (config)

Maintenant, lancez l’application sur votre appareil (pas sur un simulateur, car il n’a pas de caméra). L'alerte demandant l'autorisation de la caméra avec le message que vous avez écrit apparaîtra et vous devrez l'autoriser. Attendez un peu pendant qu'il charge l'origine du monde.

Si vous êtes ici, vous avez déjà une application AR en cours d'exécution. Toutes nos félicitations!

Comment fonctionnent les axes AR

La barre rouge ou axe X permet de positionner les objets à gauche ou à droite de l'origine du monde. La barre verte ou l’axe Y sert à positionner les objets en haut ou en bas de l’origine du monde. Et la barre bleue ou axe Z est utilisée pour déterminer la distance ou la distance séparant un objet de l’origine du monde.

Une valeur positive de X positionnera un objet à la droite de l'origine du monde et un négatif le placera à gauche. Positif pour Y le placera en haut et le négatif en bas de l’origine mondiale. Positif pour Z le placera plus près, et négatif le placera plus loin de l’origine du monde.

Ajout d'un objet virtuel

Ajoutons des objets virtuels à la scène. Une capsule 3D serait un bon choix. Déclarez un capsuleNode de type SCNNode et donnez-lui une géométrie de capsule. Donnez-lui une hauteur de 0,1 mètre et un rayon de 0,03 mètre.

let capsuleNode = SCNNode (géométrie: SCNCapsule (capRadius: 0.03, hauteur: 0.1)

Maintenant, placez-le à 0,1 mètre à gauche de l'origine mondiale, à 0,1 mètre au-dessus de l'origine mondiale et à 0,1 mètre de l'origine mondiale:

capsuleNode.position = SCNVector3 (0.1, 0.1, -0.1)

Maintenant, ajoutez le noeud à la scène:

sceneView.scene.rootNode.addChildNode (capsuleNode)

SceneView contient une scène chargée de contenir tous les objets 3D au format SCNNode qui formeront la scène 3D. Nous ajoutons la capsule au nœud racine de la scène. La position du nœud racine est exactement alignée sur la position de l’origine du monde. Cela signifie que sa position est (0,0,0).

Actuellement, notre méthode viewDidLoad ressemble à ceci:

remplacer func viewDidLoad () {
super.viewDidLoad ()
sceneView.debugOptions = [ARSCNDebugOptions.showFeaturePoints, ARSCNDebugOptions.showWorldOrigin]
sceneView.session.run (config)
let capsuleNode = SCNNode (géométrie: SCNCapsule (capRadius: 0.03, height: 0.1))
capsuleNode.position = SCNVector3 (0.1, 0.1, -0.1)
sceneView.scene.rootNode.addChildNode (capsuleNode)
}

Maintenant, lancez l'application.

Cool! Nous venons de positionner un objet virtuel dans le monde réel. Vous pouvez jouer avec différentes positions et différentes géométries pour explorer davantage. Maintenant, faisons pivoter la capsule de 90 degrés autour de l’axe Z afin qu’elle repose à plat sur l’axe X et change de couleur en bleu.

Euler Angles

Les angles d’Euler sont responsables de l’angle d’affichage d’un SCNNode. Nous verrons comment l'utiliser pour faire pivoter la capsule.

Des matériaux peuvent être ajoutés à chaque géométrie SCN, ce qui définit l'apparence de la géométrie. Les matériaux ont une propriété diffuse qui, une fois définie, étend son contenu sur toute la géométrie.

Dans viewDidLoad, ajoutez les lignes ci-dessous après avoir défini la position de la capsule.

capsuleNode.geometry? .firstMaterial? .diffuse.contents = UIColor.blue // 1
capsuleNode.eulerAngles = SCNVector3 (0,0, Double.pi / 2)
// 2

Ici, dans la première ligne, nous définissons la couleur bleue sur le tout premier matériau du noeud qui se répandra sur la capsule et lui donnera une apparence bleue. Dans la ligne 2, nous définissons l’angle de Z-Euler à 90 degrés radians. Enfin, notre vue se charge et ressemble à ceci:

remplacer func viewDidLoad () {
super.viewDidLoad ()
sceneView.debugOptions = [ARSCNDebugOptions.showFeaturePoints, ARSCNDebugOptions.showWorldOrigin]
sceneView.session.run (config)
let capsuleNode = SCNNode (géométrie: SCNCapsule (capRadius: 0.03, height: 0.1))
capsuleNode.position = SCNVector3 (0.1, 0.1, -0.1)
capsuleNode.geometry? .firstMaterial? .diffuse.contents = UIColor.blue // 1
capsuleNode.eulerAngles = SCNVector3 (0,0, Double.pi / 2) // 2
sceneView.scene.rootNode.addChildNode (capsuleNode)
}

Maintenant, lancez l'application.

Génial! Une capsule de sommeil de couleur bleue sur le mur! Vous pouvez même ajouter des textures en tant que contenu diffus pour rendre un objet plus réaliste. Nous utiliserons cela dans la section suivante lorsque nous placerons les textures des carreaux sur le sol.

Maintenant que nous avons placé avec succès des objets virtuels dans le monde réel, il est temps de décorer notre sol avec des carreaux de sol virtuels. Pour obtenir l'effet de sol, nous utiliserons une géométrie SCNPlane. SCNPlane n'a pas de profondeur comme les autres géométries 3D, ce qui en fait un ajustement parfait pour notre application.

Délégués ARSCENEView

Avant de commencer la détection au sol, nous allons explorer quelques méthodes de délégation de notre sceneView afin de comprendre les fonctionnalités dont nous disposons pour interagir avec une session AR en cours.

func renderer (SCNSceneRenderer, didAdd: SCNNode, pour: ARAnchor)

Chaque fois que nous bougeons ou inclinons notre appareil avec une session AR, l’ARKit essaie de trouver différents ARAnchors dans les environs. Un ARAnchor contient des informations sur la position et l'orientation du monde réel pouvant être utilisées pour placer un objet.

Une fois qu'un ancrage différent est trouvé, un nouveau nœud est ajouté à la scène avec les mêmes informations pour prendre en charge cet ancrage récemment trouvé. Cette méthode de délégué nous en informera. Nous allons l'utiliser pour trouver toutes les positions sur le sol pour placer les carreaux.

func renderer (_ renderer: SCNSceneRenderer, noeud didUpdate: SCNNode, pour ancre: ARAnchor)

La plupart du temps, tous les nœuds ajoutés à partir des ancres appartiennent au même objet. Supposons que vous vous déplaciez sur le sol et que l’appareil trouve un certain nombre d’ancres à différentes positions. Il essaie d'ajouter tous les nœuds pour ces ancres, car il pense que toutes ces ancres appartiennent à des objets différents.

Mais ARKit finit par reconnaître que tous appartiennent au même étage. Il met donc à jour le tout premier nœud d’étage en ajoutant les dimensions des autres nœuds en double. Cette méthode de délégué nous en informera.

func renderer (SCNSceneRenderer, didRemove: SCNNode, pour: ARAnchor)

Après la mise à jour du premier nœud unique avec les dimensions de tous les autres nœuds en double, ARKit supprime tous les nœuds en double et la méthode déléguée nous en informe. Nous utiliserons toutes les méthodes de délégation ci-dessus dans notre application (et leur objectif deviendra plus clair).

Détection de plan

Actuellement, notre scène tente de rassembler toutes les ancres rencontrées, car il s’agit du comportement par défaut. Mais comme un sol est une surface horizontale, nous ne nous intéressons qu'aux ancres qui sont sur des plans horizontaux. Donc, retournez à notre méthode viewDidLoad et écrivez le code ci-dessous avant d'exécuter la ligne session (c'est-à-dire avant sceneView.session.run (config)).

config.planeDetection = .horizontal

Dans la méthode viewDidLoad, vous pouvez tout supprimer après sceneView.session.run (config), comme c'était le cas pour placer la capsule à l'écran et nous n'en avons plus besoin. Puisque nous allons utiliser toutes les méthodes de délégation mentionnées ci-dessus, nous devons faire de notre viewController un délégué de la scèneView. Avant l'accolade fermante de la méthode viewDidLoad (), ajoutez la ligne ci-dessous.

sceneView.delegate = self

Vous devriez maintenant recevoir une erreur, car notre contrôleur de vue ne se conforme toujours pas au délégué sceneView. Pour implémenter cela, créons une extension du contrôleur de vue à la fin du fichier ViewController.swift.

extension ViewController: ARSCNViewDelegate {}

La méthode de délégué didAdd SCNNode sera déclenchée chaque fois qu'une partie de l'étage est découverte et qu'un nouveau nœud est ajouté à la scène en fonction de l'ancre. Dans cette méthode, nous allons créer un nœud de plancher et l’ajouter en tant qu’enfant du nœud récemment ajouté à la position de l’ancre.

ARArchor peut être de quatre types différents pour résoudre quatre objectifs différents. ARPlaneAnchor ne s'intéresse ici qu’à la détection des plans horizontaux ou verticaux.

Création de nœuds de plancher AR

Créons une fonction qui recevrait un ARPlaneAnchor en tant que paramètre, créons un nœud de plancher à la position de l’ancre et le renvoyons.

func createFloorNode (ancre: ARPlaneAnchor) -> SCNNode {
let floorNode = SCNNode (géométrie: SCNPlane (largeur: CGFloat (anchor.extent.x), hauteur: CGFloat (anchor.extent.z))) // 1
floorNode.position = SCNVector3 (anchor.center.x, 0, anchor.center.z) // 2
floorNode.geometry? .firstMaterial? .diffuse.contents = UIColor.blue // 3
floorNode.geometry? .firstMaterial? .isDoubleSided = true // 4
floorNode.eulerAngles = SCNVector3 (Double.pi / 2,0,0) // 5
retour floorNode // 6
}

Parcourons la fonction ligne par ligne et en discutons plus en détail. Veuillez suivre la description de chaque ligne, car c’est la partie la plus délicate.

1. Nous créons un nœud avec une géométrie de SCNPlane qui a la taille de l'ancre. L'étendue d'ARPlaneAnchor contient les informations de position. Le fait que l’extension.z ait été utilisé comme hauteur et non comme extension pourrait être un peu déroutant. Si vous visualisez qu'un cube 3D est placé sur un sol et que vous souhaitez le rendre plat le long d'une surface 2D, vous devez modifier le y en zéro et le rendre plat. Maintenant, pour obtenir la longueur de cette surface 2D, considérez le z, n’est-ce pas? Notre sol est plat, nous avons donc besoin d’un nœud plat et non d’un cube.

2. Nous définissons la position du nœud. Comme nous n’avons besoin d’aucune élévation, nous faisons y zéro.

3. Définissez la couleur du sol sur bleu.

4. La couleur du matériau ne sera affichée que sur un côté, à moins que nous n'indiquions qu'il s'agit d'un recto-verso.

5. Par défaut, l'avion sera placé verticalement. Pour le rendre horizontal, nous devons le faire pivoter de 90 degrés.

Implémentation des méthodes déléguées

Maintenant, implémentons la méthode déléguée didAdd SCNNode.

func renderer (_ renderer: SCNSceneRenderer, nœud didAdd: SCNNode, pour l'ancre: ARAnchor) {
garde laisser planeAnchor = ancre comme? ARPlaneAnchor else {retour} // 1
let planeNode = createFloorNode (anchor: planeAnchor) // 2
node.addChildNode (planeNode) // 3
}

Dans la ligne 1, nous vérifions si l'ancre est un ARPlaneAnchor, car nous ne traiterions que de ce type d'ancre.

Sur la ligne 2, un nouveau nœud est créé sur la base de l'ancre. Dans la ligne 3, il est ajouté au nœud.

Dans le délégué didUpdate SCNNode, nous allons supprimer tous nos nœuds de plancher. Nous le ferons parce que les dimensions du nœud actuel ont été modifiées et que les anciens nœuds de plancher ne correspondent pas. Ensuite, nous allons à nouveau ajouter un nouveau nœud de plancher à ce nœud mis à jour.

func renderer (_ renderer: SCNSceneRenderer, noeud didUpdate: SCNNode, pour ancre: ARAnchor) {
garde laisser planeAnchor = ancre comme? ARPlaneAnchor else {retour}
node.enumerateChildNodes {(node, _) dans
node.removeFromParentNode ()
}
let planeNode = createFloorNode (ancre: planeAnchor)
node.addChildNode (planeNode)
}

Dans la méthode déléguée didRemove SCNNode, nous souhaitons nettoyer tous nos nœuds indésirables de manière civilisée.

func renderer (_ renderer: SCNSceneRenderer, noeud didRemove: SCNNode, pour ancre: ARAnchor) {
garde laisser _ = ancre comme? ARPlaneAnchor else {retour}
node.enumerateChildNodes {(node, _) dans
node.removeFromParentNode ()
}
}

Phew! C'est ça! Lancer l'application.

Ajout de l'effet de tuile

Attends quoi? Un sol bleu? Non, nous ne sommes pas encore complètement terminés. Juste un petit changement et nous aurons un plancher magnifique!

Pour changer le sol bleu en tuiles, nous avons besoin d’une texture. Laissons Google pour une texture de carreaux de sol. J'ai cherché «texture de plancher en bois» et trouvé de belles images de texture. Enregistrez l’un d’entre eux sur votre Mac et faites-le glisser dans Assets.xcassets.

Je l'ai nommé WoodenFloorTile. Vous pouvez le nommer comme vous voulez. Retournez dans le fichier ViewController.swift. Dans la fonction createFloorNode, au lieu de définir UIColor.blue en tant que contenu diffus, définissez-la comme une UIImage avec le nom que vous avez attribué à l'image dans le dossier de ressources.

floorNode.geometry? .firstMaterial? .diffuse.contents = UIImage (nommé: "WoodenFloorTile")

Maintenant, lancez l'application et attendez que le monde d'origine soit chargé. Une fois que le sol est détecté, déplacez-vous pour mettre à jour les informations sur les nœuds.

Wow, vous avez vraiment un plancher magnifique! Vous pouvez télécharger plusieurs textures et les placer dans un listView. Cela vous permet de changer le sol en fonction de la texture sélectionnée, comme indiqué dans la première partie.

Téléchargez le projet complet de GitHub ici.

Maintenant que vous avez un beau plancher, il vous manque quelques beaux meubles pour donner un bel aspect à votre chambre! Nous y travaillerons plus tard.