iOS ARKit-Tutorial: Mit bloßen Fingern in die Luft zeichnen
Veröffentlicht: 2022-03-11Kürzlich hat Apple seine neue Augmented Reality (AR)-Bibliothek namens ARKit angekündigt. Für viele sah es wie eine weitere gute AR-Bibliothek aus, nicht wie ein technologischer Disruptor, um den man sich kümmern müsste. Betrachtet man jedoch die AR-Fortschritte der letzten Jahre, sollte man solche Schlüsse nicht vorschnell ziehen.
In diesem Beitrag erstellen wir ein lustiges ARKit-Beispielprojekt mit iOS ARKit. Der Benutzer legt seine Finger auf einen Tisch, als ob er einen Stift halten würde, tippt auf das Miniaturbild und beginnt zu zeichnen. Sobald der Benutzer fertig ist, kann er seine Zeichnung in ein 3D-Objekt umwandeln, wie in der folgenden Animation gezeigt. Der vollständige Quellcode für unser iOS-ARKit-Beispiel ist auf GitHub verfügbar.
Warum sollten wir uns jetzt um iOS ARKit kümmern?
Dass AR ein alter Begriff ist, dürfte wohl jedem erfahrenen Entwickler bewusst sein. Wir können die erste ernsthafte Entwicklung von AR auf die Zeit zurückführen, in der Entwickler Zugriff auf einzelne Frames von Webcams erhielten. Apps wurden zu dieser Zeit normalerweise verwendet, um Ihr Gesicht zu verändern. Die Menschheit brauchte jedoch nicht lange, um zu erkennen, dass die Verwandlung von Gesichtern in Hasen nicht zu ihren dringendsten Bedürfnissen gehörte, und bald verebbte der Hype!
Ich glaube, dass AR immer zwei wichtige Technologiesprünge verpasst hat, um es nützlich zu machen: Benutzerfreundlichkeit und Immersion. Wer anderen AR-Hypes nachspürt, wird das bemerken. Der AR-Hype nahm zum Beispiel wieder Fahrt auf, als Entwickler Zugriff auf einzelne Frames von mobilen Kameras erhielten. Neben der starken Rückkehr der Great Bunny Transformers sahen wir eine Welle von Apps, die 3D-Objekte auf gedruckte QR-Codes fallen lassen. Aber sie haben sich nie als Konzept durchgesetzt. Es handelte sich nicht um Augmented Reality, sondern um Augmented QR Codes.
Dann überraschte uns Google mit einem Stück Science-Fiction, Google Glass. Zwei Jahre vergingen, und als dieses erstaunliche Produkt zum Leben erweckt werden sollte, war es bereits tot! Viele Kritiker analysierten die Gründe für das Scheitern von Google Glass und machten alles Mögliche dafür verantwortlich, von sozialen Aspekten bis hin zu Googles stumpfem Ansatz bei der Einführung des Produkts. Wir kümmern uns in diesem Artikel jedoch aus einem bestimmten Grund um das Eintauchen in die Umgebung. Während Google Glass das Usability-Problem löste, war es immer noch nichts anderes als ein 2D-Bild, das in die Luft gezeichnet wurde.
Tech-Titanen wie Microsoft, Facebook und Apple haben diese harte Lektion auswendig gelernt. Im Juni 2017 kündigte Apple seine schöne iOS ARKit-Bibliothek an, die Immersion zu seiner obersten Priorität macht. Das Halten eines Telefons ist immer noch ein großer Blocker der Benutzererfahrung, aber die Lektion von Google Glass hat uns gelehrt, dass Hardware nicht das Problem ist.
Ich glaube, wir steuern sehr bald auf einen neuen AR-Hype-Höhepunkt zu, und mit diesem neuen bedeutenden Dreh- und Angelpunkt könnte es schließlich seinen Heimatmarkt finden, wodurch mehr AR-App-Entwicklung zum Mainstream werden könnte. Das bedeutet auch, dass jedes Unternehmen, das Augmented-Reality-Apps entwickelt, in der Lage sein wird, das Ökosystem und die Benutzerbasis von Apple zu nutzen.
Aber genug Geschichte, lassen Sie uns unsere Hände mit Code schmutzig machen und Apple Augmented Reality in Aktion sehen!
ARKit Immersionsfunktionen
ARKit bietet zwei Hauptfunktionen; Die erste ist die Kameraposition im 3D-Raum und die zweite die Erkennung der horizontalen Ebene. Um Ersteres zu erreichen, geht ARKit davon aus, dass Ihr Telefon eine Kamera ist, die sich im realen 3D-Raum bewegt, sodass das Ablegen eines virtuellen 3D-Objekts an einem beliebigen Punkt an diesem Punkt im realen 3D-Raum verankert wird. Und für letzteres erkennt ARKit horizontale Ebenen wie Tische, sodass Sie Objekte darauf platzieren können.
Wie erreicht ARKit dies? Dies geschieht durch eine Technik namens Visual Inertial Odometry (VIO). Keine Sorge, genau wie Unternehmer ihre Freude an der Anzahl von Kichern finden, die Sie kichern, wenn Sie die Quelle hinter ihrem Startup-Namen herausfinden, finden Forscher ihre Freude an der Anzahl von Kopfkratzern, die Sie versuchen, einen Begriff zu entschlüsseln, der ihnen einfällt Benennen ihrer Erfindungen - also lassen wir sie ihren Spaß haben und weitermachen.
VIO ist eine Technik, bei der Kamerarahmen mit Bewegungssensoren verschmolzen werden, um den Standort des Geräts im 3D-Raum zu verfolgen. Die Verfolgung der Bewegung von Kamerabildern erfolgt durch die Erkennung von Merkmalen, oder anders gesagt, Kantenpunkten im Bild mit hohem Kontrast – wie die Kante zwischen einer blauen Vase und einem weißen Tisch. Indem festgestellt wird, wie stark sich diese Punkte von einem Frame zum anderen relativ zueinander bewegt haben, kann man abschätzen, wo sich das Gerät im 3D-Raum befindet. Aus diesem Grund funktioniert ARKit nicht richtig, wenn es vor einer strukturlosen weißen Wand platziert wird oder wenn sich das Gerät sehr schnell bewegt, was zu unscharfen Bildern führt.
Erste Schritte mit ARKit in iOS
Zum Zeitpunkt des Schreibens dieses Artikels ist ARKit Teil von iOS 11, das sich noch in der Beta-Phase befindet. Um loszulegen, müssen Sie also iOS 11 Beta auf iPhone 6s oder höher und die neue Xcode Beta herunterladen. Wir können ein neues ARKit-Projekt über New > Project > Augmented Reality App starten. Ich fand es jedoch bequemer, dieses Augmented-Reality-Tutorial mit dem offiziellen Apple ARKit-Beispiel zu beginnen, das einige wichtige Codeblöcke enthält und besonders hilfreich für die Flugzeugerkennung ist. Beginnen wir also mit diesem Beispielcode, erläutern zuerst die wichtigsten Punkte darin und ändern ihn dann für unser Projekt.
Zuerst sollten wir bestimmen, welche Engine wir verwenden werden. ARKit kann mit Sprite SceneKit oder Metal verwendet werden. Im Apple ARKit-Beispiel verwenden wir iOS SceneKit, eine von Apple bereitgestellte 3D-Engine. Als nächstes müssen wir eine Ansicht einrichten, die unsere 3D-Objekte rendert. Dies erfolgt durch Hinzufügen einer Ansicht vom Typ ARSCNView
.
ARSCNView
ist eine Unterklasse der SceneKit-Hauptansicht mit dem Namen SCNView
, erweitert die Ansicht jedoch um einige nützliche Funktionen. Es rendert den Live-Video-Feed von der Gerätekamera als Szenenhintergrund, während es den SceneKit-Raum automatisch an die reale Welt anpasst, vorausgesetzt, das Gerät ist eine sich bewegende Kamera in dieser Welt.
ARSCNView
führt keine eigene AR-Verarbeitung durch, erfordert jedoch ein AR-Sitzungsobjekt, das die Gerätekamera und die Bewegungsverarbeitung verwaltet. Zu Beginn müssen wir also eine neue Sitzung zuweisen:
self.session = ARSession() sceneView.session = session sceneView.delegate = self setupFocusSquare()
Die letzte Zeile oben fügt einen visuellen Indikator hinzu, der dem Benutzer visuell hilft, den Status der Flugzeugerkennung zu beschreiben. Focus Square wird vom Beispielcode bereitgestellt, nicht von der ARKit-Bibliothek, und dies ist einer der Hauptgründe, warum wir mit diesem Beispielcode begonnen haben. Mehr dazu finden Sie in der Readme-Datei, die im Beispielcode enthalten ist. Das folgende Bild zeigt ein auf einen Tisch projiziertes Fokusquadrat:
Der nächste Schritt besteht darin, die ARKit-Sitzung zu starten. Es ist sinnvoll, die Sitzung jedes Mal neu zu starten, wenn die Ansicht angezeigt wird, da wir die vorherigen Sitzungsinformationen nicht verwenden können, wenn wir den Benutzer nicht mehr verfolgen. Also werden wir die Sitzung in viewDidAppear starten:
override func viewDidAppear(_ animated: Bool) { let configuration = ARWorldTrackingSessionConfiguration() configuration.planeDetection = .horizontal session.run(configuration, options: [.resetTracking, .removeExistingAnchors]) }
Im obigen Code beginnen wir damit, die ARKit-Sitzungskonfiguration so einzustellen, dass horizontale Ebenen erkannt werden. Zum Zeitpunkt des Schreibens dieses Artikels bietet Apple keine anderen Optionen als diese an. Aber anscheinend deutet es darauf hin, in Zukunft komplexere Objekte zu erkennen. Dann starten wir die Sitzung und stellen sicher, dass wir das Tracking zurücksetzen.
Schließlich müssen wir das Fokusquadrat immer dann aktualisieren, wenn sich die Kameraposition, dh die tatsächliche Ausrichtung oder Position des Geräts, ändert. Dies kann in der Renderer-Delegat-Funktion von SCNView erfolgen, die jedes Mal aufgerufen wird, wenn ein neuer Frame der 3D-Engine gerendert werden soll:
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) { updateFocusSquare() }
Wenn Sie die App zu diesem Zeitpunkt ausführen, sollten Sie das Fokusquadrat über dem Kamerastream sehen, der nach einer horizontalen Ebene sucht. Im nächsten Abschnitt erklären wir, wie Ebenen erkannt werden und wie wir das Fokusquadrat entsprechend positionieren können.
Flugzeuge in ARKit erkennen
ARKit kann neue Flugzeuge erkennen, vorhandene aktualisieren oder entfernen. Um Ebenen praktisch handhaben zu können, erstellen wir einen Dummy-SceneKit-Knoten, der die Informationen zur Ebenenposition und einen Verweis auf das Fokusquadrat enthält. Ebenen werden in X- und Z-Richtung definiert, wobei Y die Normale der Oberfläche ist, dh wir sollten die Positionen unserer Zeichnungsknoten immer innerhalb des gleichen Y-Werts der Ebene halten, wenn wir wollen, dass es so aussieht, als ob es auf der Ebene gedruckt wäre .
Die Flugzeugerkennung erfolgt über Callback-Funktionen, die von ARKit bereitgestellt werden. Beispielsweise wird die folgende Callback-Funktion immer dann aufgerufen, wenn ein neues Flugzeug erkannt wird:
var planes = [ARPlaneAnchor: Plane]() func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { if let planeAnchor = anchor as? ARPlaneAnchor { serialQueue.async { self.addPlane(node: node, anchor: planeAnchor) self.virtualObjectManager.checkIfObjectShouldMoveOntoPlane(anchor: planeAnchor, planeAnchorNode: node) } } } func addPlane(node: SCNNode, anchor: ARPlaneAnchor) { let plane = Plane(anchor) planes[anchor] = plane node.addChildNode(plane) } ... class Plane: SCNNode { var anchor: ARPlaneAnchor var focusSquare: FocusSquare? init(_ anchor: ARPlaneAnchor) { self.anchor = anchor super.init() } ... }
Die Callback-Funktion stellt uns zwei Parameter zur Verfügung, anchor
und node
. node
ist ein normaler SceneKit-Knoten, der an der genauen Position und Ausrichtung der Ebene platziert wird. Es hat keine Geometrie, also ist es unsichtbar. Wir verwenden es, um unseren eigenen Ebenenknoten hinzuzufügen, der ebenfalls unsichtbar ist, aber Informationen über die Ebenenausrichtung und -position im anchor
enthält.
Wie werden Position und Ausrichtung in ARPlaneAnchor
? Position, Ausrichtung und Maßstab sind alle in einer 4x4-Matrix codiert. Wenn ich die Möglichkeit habe, ein mathematisches Konzept auszuwählen, das Sie lernen sollen, werden es zweifellos Matrizen sein. Wie auch immer, wir können dies umgehen, indem wir diese 4x4-Matrix wie folgt beschreiben: Ein brillantes 2-dimensionales Array, das 4x4-Gleitkommazahlen enthält. Indem diese Zahlen auf eine bestimmte Weise mit einem 3D-Vertex v1 in seinem lokalen Raum multipliziert werden, ergibt sich ein neuer 3D-Vertex v2, der v1 im Weltraum darstellt. Wenn also v1 = (1, 0, 0) in seinem lokalen Raum ist und wir es bei x = 100 im Weltraum platzieren wollen, ist v2 gleich (101, 0, 0) in Bezug auf den Weltraum. Natürlich wird die Mathematik dahinter komplexer, wenn wir Drehungen um Achsen hinzufügen, aber die gute Nachricht ist, dass wir darauf verzichten können, sie zu verstehen (ich empfehle dringend, den entsprechenden Abschnitt dieses ausgezeichneten Artikels zu lesen, um eine ausführliche Erklärung dieses Konzepts zu erhalten ).
checkIfObjectShouldMoveOntoPlane
prüft, ob wir bereits Objekte gezeichnet haben und prüft, ob die y-Achse all dieser Objekte mit der der neu erkannten Ebenen übereinstimmt.

Nun zurück zu updateFocusSquare()
, beschrieben im vorherigen Abschnitt. Wir möchten das Fokusquadrat in der Mitte des Bildschirms halten, aber auf die nächstgelegene erkannte Ebene projizieren. Der folgende Code demonstriert dies:
func updateFocusSquare() { let worldPos = worldPositionFromScreenPosition(screenCenter, self.sceneView) self.focusSquare?.simdPosition = worldPos } func worldPositionFromScreenPosition(_ position: CGPoint, in sceneView: ARSCNView) -> float3? { let planeHitTestResults = sceneView.hitTest(position, types: .existingPlaneUsingExtent) if let result = planeHitTestResults.first { return result.worldTransform.translation } return nil }
sceneView.hitTest
sucht nach realen Ebenen, die einem 2D-Punkt in der Bildschirmansicht entsprechen, indem dieser 2D-Punkt auf die nächste darunter liegende Ebene projiziert wird. result.worldTransform
ist eine 4x4-Matrix, die alle Transformationsinformationen der erkannten Ebene enthält, während result.worldTransform.translation
eine praktische Funktion ist, die nur die Position zurückgibt.
Jetzt haben wir alle Informationen, die wir brauchen, um ein 3D-Objekt auf erkannten Oberflächen abzulegen, wenn ein 2D-Punkt auf dem Bildschirm gegeben ist. Also fangen wir an zu zeichnen.
Zeichnung
Lassen Sie uns zunächst den Ansatz zum Zeichnen von Formen erläutern, der dem Finger eines Menschen in der Computervision folgt. Das Zeichnen von Formen erfolgt, indem jeder neue Ort für den sich bewegenden Finger erkannt wird, ein Scheitelpunkt an diesem Ort abgelegt wird und jeder Scheitelpunkt mit dem vorherigen verbunden wird. Scheitelpunkte können durch eine einfache Linie oder durch eine Bezier-Kurve verbunden werden, wenn wir eine glatte Ausgabe benötigen.
Der Einfachheit halber folgen wir beim Zeichnen einem etwas naiven Ansatz. Für jede neue Position des Fingers lassen wir ein sehr kleines Kästchen mit abgerundeten Ecken und fast null Höhe auf dem erkannten Plan fallen. Es erscheint, als wäre es ein Punkt. Sobald der Benutzer mit dem Zeichnen fertig ist und die 3D-Schaltfläche auswählt, ändern wir die Höhe aller abgelegten Objekte basierend auf der Bewegung des Fingers des Benutzers.
Der folgende Code zeigt die PointNode
-Klasse, die einen Punkt darstellt:
let POINT_SIZE = CGFloat(0.003) let POINT_HEIGHT = CGFloat(0.00001) class PointNode: SCNNode { static var boxGeo: SCNBox? override init() { super.init() if PointNode.boxGeo == nil { PointNode.boxGeo = SCNBox(width: POINT_SIZE, height: POINT_HEIGHT, length: POINT_SIZE, chamferRadius: 0.001) // Setup the material of the point let material = PointNode.boxGeo!.firstMaterial material?.lightingModel = SCNMaterial.LightingModel.blinn material?.diffuse.contents = UIImage(named: "wood-diffuse.jpg") material?.normal.contents = UIImage(named: "wood-normal.png") material?.specular.contents = UIImage(named: "wood-specular.jpg") } let object = SCNNode(geometry: PointNode.boxGeo!) object.transform = SCNMatrix4MakeTranslation(0.0, Float(POINT_HEIGHT) / 2.0, 0.0) self.addChildNode(object) } . . . }
Sie werden im obigen Code feststellen, dass wir die Geometrie entlang der y-Achse um die halbe Höhe verschieben. Der Grund dafür ist sicherzustellen, dass die Unterseite des Objekts immer bei y = 0 liegt, sodass es über der Ebene erscheint.
Als Nächstes zeichnen wir in der Renderer-Callback-Funktion von SceneKit einen Indikator, der sich wie ein Stiftspitzenpunkt verhält, und verwenden dieselbe PointNode
-Klasse. Wir werden einen Punkt an dieser Stelle ablegen, wenn das Zeichnen aktiviert ist, oder die Zeichnung in eine 3D-Struktur heben, wenn der 3D-Modus aktiviert ist:
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) { updateFocusSquare() // Setup a dot that represents the virtual pen's tippoint if (self.virtualPenTip == nil) { self.virtualPenTip = PointNode(color: UIColor.red) self.sceneView.scene.rootNode.addChildNode(self.virtualPenTip!) } // Draw if let screenCenterInWorld = worldPositionFromScreenPosition(self.screenCenter, self.sceneView) { // Update virtual pen position self.virtualPenTip?.isHidden = false self.virtualPenTip?.simdPosition = screenCenterInWorld // Draw new point if (self.inDrawMode && !self.virtualObjectManager.pointNodeExistAt(pos: screenCenterInWorld)){ let newPoint = PointNode() self.sceneView.scene.rootNode.addChildNode(newPoint) self.virtualObjectManager.loadVirtualObject(newPoint, to: screenCenterInWorld) } // Convert drawing to 3D if (self.in3DMode ) { if self.trackImageInitialOrigin != nil { DispatchQueue.main.async { let newH = 0.4 * (self.trackImageInitialOrigin!.y - screenCenterInWorld.y) / self.sceneView.frame.height self.virtualObjectManager.setNewHeight(newHeight: newH) } } else { self.trackImageInitialOrigin = screenCenterInWorld } } }
virtualObjectManager
ist eine Klasse, die gezeichnete Punkte verwaltet. Im 3D-Modus schätzen wir die Differenz zur letzten Position und erhöhen/verringern die Höhe aller Punkte um diesen Wert.
Bisher zeichnen wir auf der erkannten Oberfläche unter der Annahme, dass sich der virtuelle Stift in der Mitte des Bildschirms befindet. Nun zum lustigen Teil – den Finger des Benutzers erkennen und ihn anstelle der Bildschirmmitte verwenden.
Erkennen der Fingerspitze des Benutzers
Eine der coolen Bibliotheken, die Apple in iOS 11 eingeführt hat, ist Vision Framework. Es bietet einige Computer-Vision-Techniken auf ziemlich praktische und effiziente Weise. Insbesondere werden wir die Objektverfolgungstechnik für unser Augmented-Reality-Tutorial verwenden. Die Objektverfolgung funktioniert wie folgt: Zunächst liefern wir ihr ein Bild und die Koordinaten eines Quadrats innerhalb der Bildgrenzen für das Objekt, das wir verfolgen möchten. Danach rufen wir eine Funktion auf, um das Tracking zu initialisieren. Schließlich geben wir ein neues Bild ein, in dem sich die Position dieses Objekts geändert hat, und das Analyseergebnis der vorherigen Operation. In Anbetracht dessen wird es uns den neuen Standort des Objekts zurückgeben.
Wir werden einen kleinen Trick anwenden. Wir bitten den Benutzer, seine Hand auf den Tisch zu legen, als ob er einen Stift halten würde, und sicherzustellen, dass sein Miniaturbild in Richtung Kamera zeigt, wonach er auf sein Miniaturbild auf dem Bildschirm tippen sollte. Hier sind zwei Punkte zu präzisieren. Erstens sollte das Miniaturbild genügend eindeutige Merkmale aufweisen, die über den Kontrast zwischen dem weißen Miniaturbild, der Haut und dem Tisch nachvollzogen werden können. Dies bedeutet, dass dunklere Hautpigmente zu einer zuverlässigeren Verfolgung führen. Zweitens, da der Benutzer seine Hände auf dem Tisch abstützt und wir den Tisch bereits als Ebene erkennen, führt das Projizieren der Position des Miniaturbilds aus der 2D-Ansicht in die 3D-Umgebung zu fast der exakten Position des Fingers auf dem Tisch.
Das folgende Bild zeigt Feature-Punkte, die von der Vision-Bibliothek erkannt werden könnten:
Wir werden die Thumbnail-Verfolgung in einer Tippgeste wie folgt initialisieren:
// MARK: Object tracking fileprivate var lastObservation: VNDetectedObjectObservation? var trackImageBoundingBox: CGRect? let trackImageSize = CGFloat(20) @objc private func tapAction(recognizer: UITapGestureRecognizer) { lastObservation = nil let tapLocation = recognizer.location(in: view) // Set up the rect in the image in view coordinate space that we will track let trackImageBoundingBoxOrigin = CGPoint(x: tapLocation.x - trackImageSize / 2, y: tapLocation.y - trackImageSize / 2) trackImageBoundingBox = CGRect(origin: trackImageBoundingBoxOrigin, size: CGSize(width: trackImageSize, height: trackImageSize)) let t = CGAffineTransform(scaleX: 1.0 / self.view.frame.size.width, y: 1.0 / self.view.frame.size.height) let normalizedTrackImageBoundingBox = trackImageBoundingBox!.applying(t) // Transfrom the rect from view space to image space guard let fromViewToCameraImageTransform = self.sceneView.session.currentFrame?.displayTransform(withViewportSize: self.sceneView.frame.size, orientation: UIInterfaceOrientation.portrait).inverted() else { return } var trackImageBoundingBoxInImage = normalizedTrackImageBoundingBox.applying(fromViewToCameraImageTransform) trackImageBoundingBoxInImage.origin.y = 1 - trackImageBoundingBoxInImage.origin.y // Image space uses bottom left as origin while view space uses top left lastObservation = VNDetectedObjectObservation(boundingBox: trackImageBoundingBoxInImage) }
Der schwierigste Teil oben ist, wie man die Tap-Position vom UIView-Koordinatenraum in den Bildkoordinatenraum umwandelt. ARKit stellt uns die displayTransform
-Matrix zur Verfügung, die vom Bildkoordinatenraum in einen Viewport-Koordinatenraum konvertiert, aber nicht umgekehrt. Wie können wir also das Gegenteil tun? Durch Verwendung der Inversen der Matrix. Ich habe wirklich versucht, den Einsatz von Mathematik in diesem Beitrag zu minimieren, aber in der 3D-Welt ist es manchmal unvermeidlich.
Als nächstes werden wir im Renderer ein neues Bild einspeisen, um die neue Position des Fingers zu verfolgen:
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) { // Track the thumbnail guard let pixelBuffer = self.sceneView.session.currentFrame?.capturedImage, let observation = self.lastObservation else { return } let request = VNTrackObjectRequest(detectedObjectObservation: observation) { [unowned self] request, error in self.handle(request, error: error) } request.trackingLevel = .accurate do { try self.handler.perform([request], on: pixelBuffer) } catch { print(error) } . . . }
Sobald die Objektverfolgung abgeschlossen ist, wird eine Callback-Funktion aufgerufen, in der wir den Speicherort der Miniaturansicht aktualisieren. Es ist normalerweise die Umkehrung des Codes, der in die Tap-Erkennung geschrieben ist:
fileprivate func handle(_ request: VNRequest, error: Error?) { DispatchQueue.main.async { guard let newObservation = request.results?.first as? VNDetectedObjectObservation else { return } self.lastObservation = newObservation var trackImageBoundingBoxInImage = newObservation.boundingBox // Transfrom the rect from image space to view space trackImageBoundingBoxInImage.origin.y = 1 - trackImageBoundingBoxInImage.origin.y guard let fromCameraImageToViewTransform = self.sceneView.session.currentFrame?.displayTransform(withViewportSize: self.sceneView.frame.size, orientation: UIInterfaceOrientation.portrait) else { return } let normalizedTrackImageBoundingBox = trackImageBoundingBoxInImage.applying(fromCameraImageToViewTransform) let t = CGAffineTransform(scaleX: self.view.frame.size.width, y: self.view.frame.size.height) let unnormalizedTrackImageBoundingBox = normalizedTrackImageBoundingBox.applying(t) self.trackImageBoundingBox = unnormalizedTrackImageBoundingBox // Get the projection if the location of the tracked image from image space to the nearest detected plane if let trackImageOrigin = self.trackImageBoundingBox?.origin { self.lastFingerWorldPos = self.virtualObjectManager.worldPositionFromScreenPosition(CGPoint(x: trackImageOrigin.x - 20.0, y: trackImageOrigin.y + 40.0), in: self.sceneView) } } }
Schließlich verwenden wir beim Zeichnen self.lastFingerWorldPos
anstelle von screen center, und wir sind fertig.
ARKit und die Zukunft
In diesem Beitrag haben wir gezeigt, wie AR durch die Interaktion mit Benutzerfingern und realen Tischen immersiv sein kann. Mit weiteren Fortschritten in der Computervision und durch das Hinzufügen von mehr AR-freundlicher Hardware zu Geräten (wie Tiefenkameras) können wir auf die 3D-Struktur von immer mehr Objekten um uns herum zugreifen.
Obwohl noch nicht für die Massen freigegeben, ist es erwähnenswert, dass Microsoft es sehr ernst meint, das AR-Rennen durch sein Hololens-Gerät zu gewinnen, das AR-maßgeschneiderte Hardware mit fortschrittlichen 3D-Umgebungserkennungstechniken kombiniert. Sie können abwarten, wer dieses Rennen gewinnen wird, oder Sie können ein Teil davon sein, indem Sie jetzt wirklich immersive Augmented-Reality-Apps entwickeln! Aber bitte tut der Menschheit einen Gefallen und verwandelt keine lebenden Objekte in Hasen.