Sådan kommer du i gang med augmented reality i Swift, den nemme måde

Hvis du ser dig omkring, er dette den gyldne æra inden for teknologi. Hver nøgle tilføjer noget nyt til den eksisterende stak af teknologier. Det er spændende at se, hvordan disse nye teknologier har forbedret grænserne for vores fantasi. Som udvikler skal vi være stolte af, at vi er førstehåndsbrugere af disse teknologier.

Men hver ny teknologi kommer med en ganske stejl indlæringskurve. Du kan bare ikke se en keynote eller en video på Youtube og begynde at udvikle en app. Men den gode nyhed er, at det med AR i Swift er bemærkelsesværdigt nemt at arbejde med grundlæggende AR-apps. Apple har gjort det meste af det tunge løft for dig. Følg med og du vil se, hvor let det kan være.

Lad os grave ind ...

I denne vejledning lærer vi de nødvendige værktøjer og teknikker fra AR i Swift, der giver os mulighed for at oprette en app, der dekorerer dit gulv med nogle seje gulvfliser og træteksturer. Den færdige app vil se sådan ud:

Lad os starte med at oprette en enkelt visningsapplikation i Xcode og navngive den Home Decor.

Tilføjelse af kameratilladelser

Nu er den allerførste ting, vi vil gøre, at navigere til info.plist-filen og aktivere kamerabrug. Kamerafunktioner er den første ting, du har brug for til en AR-app. Find tasten til brug af kamerabeskrivelse, som billedet nedenfor, og giv det en passende besked. Denne besked vises i den allerførste lancering af appen, mens du beder om kameratilladelser fra brugeren.

Tilføjelse af ARKit-muligheder til appen

Gå til Main.storyboard. Træk og slip en ARKit SceneKit-visning på ViewController, og fastgør ARSCNView til kanterne af ViewController.

Opret en IBOutlet til ViewController-klassen, og navngiv den sceneView. Så snart du gør det, en fejlmeddelelse om sort ARSCNView , dukker op, da vores opfattelse controller ikke anerkender noget af type ARSCNView. For at løse dette og bruge andre ARKit-funktioner er vi nødt til at importere ARKit til visningscontrolleren.

Flyt nu fra storyboard til view controller.swift-filen. Erklær en egenskab af typen ARWorldTrackingConfiguration før metoden viewDidLoad (), og navngiv den config. Og vores visningscontroller vil se sådan ud (jeg har fjernet didReceiveMemoryWarning-metoden):

import UIKitimport ARKit
class ViewController: UIViewController {
@IBOutlet weak var sceneView: ARSCNView!let config = ARWorldTrackingConfiguration()
override func viewDidLoad() {super.viewDidLoad()}

Tillad fejlretning

Denne konfigurationsvariabel bestemmer konfigurationen af ​​scenesessionen. Vi ser brugen senere i sektionen. Tilføj nu følgende i metoden viewDidLoad efter super.viewDidLoad ():

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

Her muliggør vi fejlfindingsindstillinger for vores sceneView, som ikke er andet end kameravisningen med AR-rammens muligheder. ARSCNDebugOptions.showWorldOrigin viser verdens oprindelse på skærmen. Dette hjælper os med at finde referencepunktet for alle andre positioner. ARSCNDebugOptions.showFeaturePoints viser alle de punkter på skærmen, som AR-kameraet har genkendt i omgivelserne.

Nu for at starte AR-sessionen skal vi køre en session i vores sceneView med de konfigurationer, der er nævnt i konfigurationsvariablen. Lige under sceneView.debugOptions-linjen, skriv:

sceneView.session.run(config)

Kør nu appen på din enhed (ikke på en simulator, da den ikke har kameraet). Alarmen, der beder om kameratilladelse med den besked, du skrev, vises, og du skal tillade det. Vent lidt, mens den indlæser verdensoprindelsen.

Hvis du er her, har du allerede en AR-app, der kører. Tillykke!

Sådan fungerer AR Axes

Den røde bjælke eller X-aksen bruges til at placere objekter til venstre eller højre for verdensoprindelsen. Den grønne bjælke eller Y-aksen bruges til at placere objekter til toppen eller bunden af ​​verdens oprindelse. Og den blå bjælke eller Z-aksen bruges til at bestemme, hvor tæt eller langt et objekt skal placeres fra verdens oprindelsen.

En positiv værdi af X placerer et objekt til højre for verdensoprindelsen, og negativt placerer det til venstre. Positivt for Y vil placere det øverst og negativt placere det på bunden af ​​verdens oprindelse. Positivt for Z vil placere det nærmere, og negativt placere det længere væk fra verdens oprindelsen.

Tilføjelse af et virtuelt objekt

Lad os tilføje nogle virtuelle objekter til scenen. 3D-kapsel ville være et godt valg. Erklær en kapselNode af typen SCNNode, og giv den en kapselgeometri. Giv den en højde på 0,1 meter og en radius på 0,03 meter.

let capsuleNode = SCNNode(geometry: SCNCapsule(capRadius: 0.03, height: 0.1

Placer den nu 0,1 meter tilbage af verdens oprindelsen, 0,1 meter over verdens oprindelsen og 0,1 meter væk fra verdens oprindelsen:

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

Føj nu noden til scenen:

sceneView.scene.rootNode.addChildNode(capsuleNode)

SceneView indeholder en scene, der er ansvarlig for at holde alle 3D-objekter i SCNNode-format, der vil danne 3D-scenen. Vi tilføjer kapslen til scenens rodknude. Rodknudeposition er nøjagtigt justeret til positionen for verdens oprindelse. Det betyder, at dets position er (0,0,0).

I øjeblikket ser vores viewDidLoad-metode sådan ud:

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

Kør nu appen.

Fedt nok! Vi har lige placeret et virtuelt objekt i den virkelige verden. Du kan lege med forskellige positioner og forskellige geometrier for at udforske mere. Lad os nu dreje kapslen 90 grader omkring Z-aksen, så den ligger fladt på X-aksen og ændrer sin farve til blå.

Euler vinkler

Euler Angles er ansvarlige for en SCNNodes displayvinkel. Vi vil se, hvordan vi bruger den til at rotere kapslen.

Hver SCNGeometry kan have tilføjet materialer til det, som definerer geometriens udseende. Materialer har en diffus egenskab, som, når den er indstillet, spreder dens indhold over hele geometrien.

I viewDidLoad skal du tilføje nedenstående linjer, når du har indstillet kapselens position.

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

Her, i første linje, indstiller vi den blå farve til det allerførste materiale i noden, der spredes over kapslen og får den til at se blå ud. I linje 2 indstiller vi Z Euler-vinklen til 90 graders radianer. Endelig indlæses og ser vores syn sådan ud:

override func viewDidLoad() {
super.viewDidLoad()
sceneView.debugOptions = [ARSCNDebugOptions.showFeaturePoints, ARSCNDebugOptions.showWorldOrigin]
sceneView.session.run(config)
let capsuleNode = SCNNode(geometry: 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)
}

Kør nu appen.

Store! En blåfarvet sovekapsel på væggen! Du kan endda tilføje teksturer som diffust indhold for at få et objekt til at se mere realistisk ud. Vi bruger det i det næste afsnit, når vi placerer flisernes teksturer på gulvet.

Nu hvor vi med succes har placeret virtuelle objekter i den virkelige verden, er det tid til at dekorere vores rigtige gulv med virtuelle gulvfliser. For at opnå gulveffekten bruger vi en SCNPlane-geometri. SCNPlane har ingen dybde som andre 3D-geometrier, hvilket gør det perfekt til vores app.

ARSCENEView delegater

Før vi begynder gulvdetekteringen, vil vi udforske nogle delegerede metoder til vores sceneView for at forstå, hvilke muligheder vi er udstyret med til at interagere med en igangværende AR-session.

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

Når vi flytter eller vipper vores enhed med en AR-session på, forsøger ARKit at finde forskellige ARAnchors i omgivelserne. En ARAnchor indeholder information om en rigtig verdensposition og orientering, der kan bruges til at placere et objekt.

Når et andet anker er fundet, tilføjes en ny node til scenen med de samme oplysninger for at imødekomme dette nyligt fundne anker. Denne delegerede metode vil informere os om det. Vi bruger den til at finde alle positioner på gulvet til placering af fliserne.

func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor)

Det meste af tiden hører alle de noder, der bliver tilføjet fra ankerne, til det samme objekt. Lad os sige, at du bevæger dig rundt på gulvet, og enheden finder et antal ankre på forskellige positioner. Det forsøger at tilføje alle noder til disse ankre, da det mener, at alle disse ankre tilhører forskellige objekter.

Men ARKit anerkender til sidst, at de alle tilhører samme etage, så det opdaterer den allerførste etage node ved at tilføje dimensioner af andre duplikerede noder. Denne delegerede metode vil informere os om det.

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

Efter opdatering af den første unikke node med dimensioner for alle andre duplikatknudepunkter fjerner ARKit alle duplikatknudepunkterne, og delegatmetoden giver os besked. Vi bruger alle ovenstående delegerede metoder i vores app (og deres formål bliver tydeligere).

Plandetektering

I øjeblikket forsøger vores scene at samle alle ankre, som den kommer på tværs af, da det er standardadfærden. Men da et gulv er en vandret overflade, er vi kun interesserede i ankre, der er på vandrette plan. Så gå tilbage til vores viewDidLoad-metode og skriv nedenstående kode, inden du kører sessionen (det er før sceneView.session.run (config)).

config.planeDetection = .horizontal

I viewDidLoad-metoden kan du fjerne alt efter sceneView.session.run (config), som det var til at placere kapslen på skærmen, og vi har ikke brug for det længere. Da vi bruger alle de ovennævnte delegerede metoder, skal vi gøre vores viewController til en delegeret af sceneView. Før nedenstående afstivning af viewDidLoad () -metoden skal du tilføje nedenstående linje.

sceneView.delegate = self

Du skal få en fejl nu, da vores visningscontroller stadig ikke er i overensstemmelse med sceneView-delegaten. For at implementere dette, lad os oprette en udvidelse af visningscontrolleren i slutningen af ​​ViewController.swift-filen.

extension ViewController:ARSCNViewDelegate{}

DidAdd SCNNode-delegatmetoden fyres hver gang en del af gulvet opdages, og en ny node føjes til scenen baseret på ankeret. Inden for denne metode opretter vi en gulvknude og tilføjer den som et barn af den nyligt tilføjede knude i ankerpositionen.

ARArchor kan være af fire forskellige typer for at løse fire forskellige formål. Her er vi kun interesseret i ARPlaneAnchor, der registrerer de vandrette eller lodrette planer.

Oprettelse af AR-gulvknuder

Lad os oprette en funktion, der modtager en ARPlaneAnchor som parameter, oprette en gulvknude ved ankerpositionen og returnere den.

func createFloorNode(anchor:ARPlaneAnchor) ->SCNNode{
let floorNode = SCNNode(geometry: SCNPlane(width: CGFloat(anchor.extent.x), height: 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
return floorNode //6
}

Lad os gå gennem funktionen linje for linje og diskutere det mere detaljeret. Følg hver linjes beskrivelse, da det er den sværeste del.

1. Vi opretter en node med en geometri af SCNPlane, der har størrelsen på ankeret. ARPlaneAnchor's omfang holder positionoplysningerne. Det faktum, at udstrækningen.z er blevet brugt som højde og ikke udstrækning.y, kan være lidt forvirrende. Hvis du visualiserer, at en 3D-terning er placeret på et gulv, og du vil gøre den flad langs en 2D-overflade, vil du ændre y til nul, og det vil gå fladt. For at få længden af ​​denne 2D-overflade vil du overveje z, ikke? Vores gulv er fladt, så vi har brug for en flad knude, ikke en terning.

2. Vi indstiller positionen for noden. Da vi ikke har brug for nogen højde, gør vi y nul.

3. Indstil gulvfarven til blå.

4. Materialefarven vises kun på den ene side, medmindre vi specifikt nævner, at den er dobbeltsidet.

5. Som standard placeres flyet lodret. For at gøre det vandret skal vi dreje det 90 grader.

Implementering af delegerede metoder

Lad os nu implementere didAdd SCNNode delegeret metode.

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
guard let planeAnchor = anchor as? ARPlaneAnchor else {return} //1
let planeNode = createFloorNode(anchor: planeAnchor) //2
node.addChildNode(planeNode) //3
}

I linje 1 kontrollerer vi, om ankeret er et ARPlaneAnchor, da vi kun beskæftiger os med denne type anker.

I linje 2 oprettes en ny node baseret på ankeret. I linje 3 bliver det tilføjet til noden.

Nu i didUpdate SCNNode-delegaten sletter vi alle vores gulvnoder. Vi gør dette, fordi dimensionerne på den aktuelle node er blevet ændret, og de gamle gulvknuder ikke stemmer overens. Derefter tilføjer vi igen en frisk gulvknude til denne opdaterede knude.

func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
guard let planeAnchor = anchor as? ARPlaneAnchor else {return}
node.enumerateChildNodes { (node, _) in
node.removeFromParentNode()
}
let planeNode = createFloorNode(anchor: planeAnchor)
node.addChildNode(planeNode)
}

I didRemove SCNNode delegeret metode ønsker vi at rense alle vores junk noder på en civiliseret måde.

func renderer(_ renderer: SCNSceneRenderer, didRemove node: SCNNode, for anchor: ARAnchor) {
guard let _ = anchor as? ARPlaneAnchor else {return}
node.enumerateChildNodes { (node, _) in
node.removeFromParentNode()
}
}

Pis! Det er det! Kør appen.

Tilføjelse af fliseeffekten

Vent, hvad? Et blåt gulv? Nej, vi er ikke helt færdige endnu. Bare en lille ændring, og vi får et fantastisk gulv!

For at ændre det blå gulv til fliser har vi brug for en struktur. Lad os google efter en gulvflise struktur. Jeg søgte efter "trægulvstruktur" og fandt nogle smukke teksturbilleder. Gem nogen af ​​dem på din Mac, og træk den til Assets.xcassets.

Jeg kaldte det WoodenFloorTile. Du kan navngive det, hvad du vil. Tilbage til ViewController.swift-filen igen. I createFloorNode-funktionen skal du i stedet for at indstille UIColor.blue som diffust indhold gøre det til et UII-billede med det navn, du har givet billedet i aktivmappen.

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

Kør nu appen, og vent, indtil verdensoprindelsen indlæses. Når gulvet er registreret, skal du bevæge dig rundt for at opdatere nodens oplysninger.

Wow, du har virkelig et smukt gulv! Du kan downloade flere teksturer og placere dem i en listevisning. Dette giver dig mulighed for at ændre gulvet baseret på den valgte tekstur, som det blev vist i første del.

Download det komplette projekt fra GitHub her.

Nu hvor du har et dejligt gulv, skal du mangle nogle pæne møbler for at give dit værelse et godt udseende! Vi vil arbejde på det senere.