Aan de slag met augmented reality in Swift, op de gemakkelijke manier

Als je om je heen kijkt, is dit het gouden tijdperk van de technologie. Elke keynote voegt iets nieuws toe aan de bestaande stapel technologieën. Het is opwindend om te zien hoe deze opkomende technologieën de grenzen van onze verbeelding hebben vergroot. Als ontwikkelaar moeten we er trots op zijn dat we de eerste handgebruikers zijn van deze technologieën.

Maar elke nieuwe technologie heeft een behoorlijk steile leercurve. Je kunt gewoon geen keynote of video op YouTube bekijken en beginnen met het ontwikkelen van een app. Maar het goede nieuws is dat het met AR in Swift opmerkelijk eenvoudig is om met eenvoudige AR-apps te werken. Apple heeft het meeste zware werk voor je gedaan. Volg mee en je zult zien hoe gemakkelijk het kan zijn.

Laten we eens kijken ...

In deze tutorial zullen we de nodige tools en technieken van AR in Swift leren waarmee we een app kunnen maken die je vloer versiert met coole vloertegels en houten texturen. De voltooide app ziet er ongeveer zo uit:

Laten we beginnen met het maken van een applicatie met één weergave in Xcode en deze Home Decor noemen.

Camerarechten toevoegen

Het allereerste dat we nu zullen doen, is naar het info.plist-bestand navigeren en cameragebruik inschakelen. Cameramogelijkheden zijn het eerste dat u nodig heeft voor een AR-app. Zoek de toets Beschrijving cameragebruik, zoals de onderstaande afbeelding, en geef deze een passend bericht. Dit bericht verschijnt bij de allereerste keer opstarten van de app terwijl de gebruiker om de cameratoestemmingen vraagt.

ARKit-mogelijkheden aan de app toevoegen

Ga naar Main.storyboard. Versleep een ARKit SceneKit View op de ViewController en zet de ARSCNView vast aan de randen van de ViewController.

Maak een IBOutlet voor de klasse ViewController en noem deze sceneView. Zodra je dat doet, een fout verklarend zwartwerk ARSCNView , popup, zoals onze view controller heeft niets van het type ARSCNView herkennen. Om dit op te lossen en om andere ARKit-functies te gebruiken, moeten we ARKit in de view controller importeren.

Ga nu van het storyboard naar het bestand view controller.swift. Declareer een eigenschap van het type ARWorldTrackingConfiguration vóór de viewDidLoad () - methode en noem deze config. En onze view-controller ziet er als volgt uit (ik heb de methode didReceiveMemoryWarning verwijderd):

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

Sta foutopsporing toe

Deze configuratievariabele bepaalt de configuraties van de scènesessie. We zullen het gebruik later in de sectie zien. Voeg nu in de viewDidLoad-methode na super.viewDidLoad () het volgende toe:

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

Hier schakelen we debug-opties in voor onze sceneView, die niets anders is dan de cameraweergave met de mogelijkheden van AR-framework. ARSCNDebugOptions.showWorldOrigin zal de oorsprong van de wereld op het scherm weergeven. Dit zal ons helpen het referentiepunt van alle andere posities te vinden. ARSCNDebugOptions.showFeaturePoints geeft alle punten op het scherm weer die de AR-camera in de omgeving heeft herkend.

Om de AR-sessie te starten, moeten we een sessie uitvoeren in onze sceneView met de configuraties die worden genoemd in de configuratievariabele. Schrijf net onder de regel sceneView.debugOptions:

sceneView.session.run(config)

Start nu de app op uw apparaat (niet op een simulator, omdat deze de camera niet heeft). De waarschuwing waarin om cameratoestemming wordt gevraagd met het bericht dat u hebt geschreven, wordt weergegeven en u moet dit toestaan. Wacht een tijdje terwijl het de wereldoorsprong laadt.

Als je hier bent, heb je al een AR-app actief. Gefeliciteerd!

Hoe AR-assen werken

De rode balk of X-as wordt gebruikt om objecten links of rechts van de wereldoorsprong te plaatsen. De groene balk of Y-as wordt gebruikt om objecten naar de boven- of onderkant van de wereldoorsprong te plaatsen. En de blauwe balk of Z-as wordt gebruikt om te bepalen hoe dicht of ver een object van de oorsprong van de wereld zal worden geplaatst.

Een positieve waarde van X plaatst een object rechts van de wereldoorsprong en negatief plaatst het aan de linkerkant. Positief voor Y plaatst het bovenaan en negatief plaatst het onderaan de wereldoorsprong. Positief voor Z zal het dichterbij plaatsen, en negatief zal het verder van de wereldoorsprong plaatsen.

Een virtueel object toevoegen

Laten we enkele virtuele objecten aan de scène toevoegen. 3D-capsule zou een goede keuze zijn. Declareer een capsuleNode van het type SCNNode en geef deze de geometrie van capsule. Geef het een hoogte van 0,1 meter en een straal van 0,03 meter.

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

Plaats het nu 0,1 meter links van de wereldoorsprong, 0,1 meter boven de wereldoorsprong en 0,1 meter verwijderd van de wereldoorsprong:

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

Voeg nu het knooppunt toe aan de scène:

sceneView.scene.rootNode.addChildNode(capsuleNode)

De sceneView bevat een scène die verantwoordelijk is voor het vasthouden van alle 3D-objecten in SCNNode-indeling die de 3D-scène zullen vormen. We voegen de capsule toe aan het hoofdknooppunt van de scène. De positie van het rootknooppunt is exact uitgelijnd met de positie van de wereldoorsprong. Dat betekent dat zijn positie (0,0,0) is.

Momenteel ziet onze viewDidLoad-methode er als volgt uit:

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)
}

Start nu de app.

Stoer! We hebben zojuist een virtueel object in de echte wereld gepositioneerd. Je kunt met verschillende posities en verschillende geometrieën spelen om meer te ontdekken. Laten we nu de capsule 90 graden rond de Z-as draaien, zodat deze plat op de X-as ligt en de kleur verandert in blauw.

Euler-hoeken

Euler Angles zijn verantwoordelijk voor de weergavehoek van een SCNNode. We zullen zien hoe we het kunnen gebruiken om de capsule te draaien.

Aan elke SCNGeometry kunnen materialen worden toegevoegd, die het uiterlijk van de geometrie bepalen. Materialen hebben een diffuse eigenschap die, wanneer ze zijn uitgehard, de inhoud over de geometrie verspreidt.

Voeg in viewDidLoad de onderstaande regels toe nadat u de positie van de capsule hebt ingesteld.

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

Hier, in de eerste regel, stellen we de blauwe kleur in op het allereerste materiaal van het knooppunt dat zich over de capsule zal verspreiden en het blauw zal laten lijken. In regel 2 stellen we de Z Euler-hoek in op 90 graden radialen. Eindelijk wordt onze weergave geladen en ziet het er als volgt uit:

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)
}

Start nu de app.

Super goed! Een blauw gekleurde slaapcapsule voor aan de muur! U kunt zelfs texturen toevoegen als diffuse inhoud om een ​​object er realistischer uit te laten zien. We zullen dat gebruiken in de volgende sectie wanneer we de texturen van de tegels op de vloer plaatsen.

Nu we met succes virtuele objecten in de echte wereld hebben geplaatst, is het tijd om onze echte vloer te versieren met virtuele vloertegels. Om het vloereffect te bereiken, gebruiken we een SCNPlane-geometrie. SCNPlane heeft geen diepte zoals andere 3D-geometrieën, waardoor het perfect past in onze app.

ARSCENE Bekijk afgevaardigden

Voordat we beginnen met de vloerdetectie, zullen we enkele gedelegeerde methoden van onze sceneView onderzoeken om te begrijpen met welke mogelijkheden we zijn uitgerust om te communiceren met een lopende AR-sessie.

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

Telkens wanneer we ons apparaat verplaatsen of kantelen met een AR-sessie erin, probeert de ARKit verschillende ARAnchors in de omgeving te vinden. Een ARAnchor bevat informatie over een werkelijke positie en oriëntatie die kan worden gebruikt om een ​​object te plaatsen.

Zodra een ander anker is gevonden, wordt een nieuw knooppunt aan de scène toegevoegd met dezelfde informatie om plaats te bieden aan dit nieuw gevonden anker. Deze gedelegeerde methode zal ons daarover informeren. We zullen het gebruiken om alle posities op de vloer te vinden om de tegels te plaatsen.

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

Meestal behoren alle knooppunten die worden toegevoegd vanuit de ankers tot hetzelfde object. Stel dat u zich over de vloer beweegt en het apparaat vindt een aantal ankers op verschillende posities. Het probeert alle knooppunten voor die ankers toe te voegen, omdat het denkt dat al deze ankers bij verschillende objecten horen.

Maar ARKit erkent uiteindelijk dat ze allemaal tot dezelfde verdieping behoren, dus het werkt het knooppunt op de eerste verdieping bij door dimensies van andere dubbele knooppunten toe te voegen. Deze gedelegeerde methode zal ons daarover informeren.

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

Na het bijwerken van het eerste unieke knooppunt met de afmetingen van alle andere dubbele knooppunten, verwijdert ARKit alle dubbele knooppunten en stelt de gedelegeerde methode ons hiervan op de hoogte. We zullen alle bovenstaande gedelegeerde methoden in onze app gebruiken (en hun doel zal duidelijker worden).

Vliegtuig detectie

Momenteel probeert onze scène alle ankers te verzamelen die hij tegenkomt, omdat dat het standaardgedrag is. Maar aangezien een vloer een horizontaal oppervlak is, zijn we alleen geïnteresseerd in ankers die op horizontale vlakken staan. Ga dus terug naar onze viewDidLoad-methode en schrijf de onderstaande code voordat u de sessie start (dat wil zeggen vóór sceneView.session.run (config)) regel.

config.planeDetection = .horizontal

In de viewDidLoad-methode kun je alles verwijderen na sceneView.session.run (config) zoals dat was om de capsule op het scherm te plaatsen en dat hebben we niet meer nodig. Aangezien we alle bovengenoemde delegate-methoden zullen gebruiken, moeten we onze viewController een gedelegeerde van de sceneView maken. Voeg de onderstaande regel toe vóór de sluitende accolade van de methode viewDidLoad ().

sceneView.delegate = self

U zou nu een foutmelding moeten krijgen, aangezien onze view controller nog steeds niet voldoet aan de sceneView delegate. Om dit te implementeren, maken we een extensie van de view controller aan het einde van het ViewController.swift-bestand.

extension ViewController:ARSCNViewDelegate{}

De didAdd SCNNode delegate-methode wordt geactiveerd elke keer dat een deel van de verdieping wordt ontdekt en er wordt een nieuw knooppunt aan de scène toegevoegd op basis van het anker. Binnen deze methode zullen we een vloerknooppunt maken en deze toevoegen als een kind van het recent toegevoegde knooppunt op de positie van het anker.

ARArchor kan uit vier verschillende typen bestaan ​​om vier verschillende doelen op te lossen. Hier zijn we alleen geïnteresseerd in ARPlaneAnchor dat de horizontale of verticale vlakken detecteert.

AR-vloerknooppunten maken

Laten we een functie maken die een ARPlaneAnchor als parameter ontvangt, een vloerknooppunt maken op de positie van het anker en deze retourneren.

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
}

Laten we de functie regel voor regel doornemen en deze in meer detail bespreken. Volg de beschrijving van elke regel, want dit is het lastigste deel.

1. We maken een knoop met een geometrie van SCNPlane die de grootte heeft van het anker. De omvang van ARPlaneAnchor bevat de positie-informatie. Het feit dat de omvang.z is gebruikt als hoogte en niet als omvang.y, kan een beetje verwarrend zijn. Als je visualiseert dat een 3D-kubus op een vloer wordt geplaatst en je deze vlak langs een 2D-oppervlak wilt maken, verander je de y in nul en wordt hij plat. Om de lengte van dit 2D-oppervlak te krijgen, zou je de z overwegen, nietwaar? Onze vloer is vlak, dus we hebben een plat knooppunt nodig, geen kubus.

2. We stellen de positie van het knooppunt in. Omdat we geen verhoging nodig hebben, maken we y nul.

3. Zet de vloerkleur op blauw.

4. De materiaalkleur wordt slechts aan één zijde weergegeven, tenzij we specifiek vermelden dat deze dubbelzijdig is.

5. Standaard wordt het vlak verticaal geplaatst. Om het horizontaal te maken, moeten we het 90 graden draaien.

Implementatie van de gedelegeerde methoden

Laten we nu de gedelegeerde methode didAdd SCNNode implementeren.

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
}

In regel 1 controleren we of het anker een ARPlaneAnchor is, aangezien we alleen dit type anker zouden behandelen.

In regel 2 wordt een nieuw knooppunt gemaakt op basis van het anker. In regel 3 wordt het toegevoegd aan het knooppunt.

Nu zullen we in de didUpdate SCNNode-gedelegeerde al onze verdiepingsknooppunten verwijderen. We doen dit omdat de afmetingen van het huidige knooppunt zijn gewijzigd en de oude vloerknooppunten niet overeenkomen. Dan zullen we weer een nieuw vloerknooppunt toevoegen aan dit bijgewerkte knooppunt.

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)
}

In didRemove SCNNode delegate-methode willen we al onze junk-nodes op een beschaafde manier opschonen.

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

Opluchting! Dat is het! Start de app.

Het tegeleffect toevoegen

Wacht wat? Een blauwe vloer? Nee, we zijn nog niet helemaal klaar. Een kleine verandering en we hebben een prachtige vloer!

Om de blauwe vloer in tegels te veranderen, hebben we een textuur nodig. Laten we googelen voor een vloertegeltextuur. Ik zocht naar "houten vloer textuur" en vond enkele mooie textuurafbeeldingen. Bewaar ze allemaal op uw Mac en sleep deze naar Assets.xcassets.

Ik noemde het WoodenFloorTile. U kunt het noemen wat u maar wilt. Terug naar het ViewController.swift-bestand opnieuw. In de createFloorNode-functie, in plaats van UIColor.blue als diffuse inhoud in te stellen, maakt u er een UIImage van met de naam die u aan de afbeelding in de asset-map hebt gegeven.

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

Start nu de app en wacht tot de wereldoorsprong is geladen. Zodra de verdieping is gedetecteerd, beweegt u zich rond om de knooppuntinformatie bij te werken.

Wauw, je hebt echt een prachtige vloer! U kunt meerdere texturen downloaden en deze in een lijstweergave plaatsen. Hiermee kunt u de vloer wijzigen op basis van de geselecteerde textuur, zoals deze in het eerste deel werd getoond.

Download hier het volledige project van GitHub.

Nu je een mooie vloer hebt, moet je een aantal leuke meubels missen om je kamer een geweldige uitstraling te geven! Daar zullen we later aan werken.