Samouczek ARKit na iOS: rysowanie w powietrzu gołymi palcami

Opublikowany: 2022-03-11

Niedawno Apple ogłosiło nową bibliotekę rozszerzonej rzeczywistości (AR) o nazwie ARKit. Dla wielu wyglądało to jak kolejna dobra biblioteka AR, a nie przełomowa technologia, o którą trzeba się troszczyć. Jeśli jednak przyjrzeć się postępowi AR w ciągu ostatnich kilku lat, nie należy zbyt szybko wyciągać takich wniosków.

Ilustracja samouczka ARKit: Interakcja z wirtualnymi obiektami w aplikacji ARKit na iOS

W tym poście stworzymy zabawny przykładowy projekt ARKit za pomocą iOS ARKit. Użytkownik położy palce na stole tak, jakby trzymał długopis, stuka w miniaturę i zaczyna rysować. Po zakończeniu użytkownik będzie mógł przekształcić swój rysunek w obiekt 3D, jak pokazano na poniższej animacji. Pełny kod źródłowy naszego przykładu iOS ARKit jest dostępny na GitHub.

Demonstracja użycia naszej przykładowej aplikacji rozszerzonej rzeczywistości ARKit na iOS

Dlaczego powinniśmy teraz przejmować się iOS ARKit?

Każdy doświadczony programista zapewne zdaje sobie sprawę z tego, że AR to stara koncepcja. Pierwszy poważny rozwój AR możemy przypiąć do czasu, gdy twórcy uzyskali dostęp do poszczególnych klatek z kamer internetowych. Aplikacje w tamtych czasach były zwykle używane do przekształcania Twojej twarzy. Jednak ludzkość szybko zdała sobie sprawę, że przekształcanie twarzy w króliki nie jest jedną z ich najpilniejszych potrzeb i wkrótce ten szum zniknął!

Uważam, że AR zawsze brakowało dwóch kluczowych skoków technologicznych, aby była użyteczna: użyteczności i immersji. Jeśli prześledziłeś inne szumy związane z AR, zauważysz to. Na przykład szum AR znów się pojawił, gdy programiści uzyskali dostęp do poszczególnych klatek z kamer mobilnych. Oprócz silnego powrotu wielkich transformatorów króliczych, widzieliśmy falę aplikacji, które upuszczają obiekty 3D na wydrukowane kody QR. Ale nigdy nie wystartowały jako koncepcja. Nie były to rozszerzona rzeczywistość, ale rozszerzone kody QR.

Następnie Google zaskoczył nas kawałkiem science fiction, Google Glass. Minęły dwa lata i zanim ten niesamowity produkt miał ożyć, był już martwy! Wielu krytyków przeanalizowało przyczyny niepowodzenia Google Glass, obwiniając wszystko, od aspektów społecznych po nudne podejście Google do wprowadzania produktu na rynek. Jednak w tym artykule obchodzi nas jeden szczególny powód - zanurzenie się w środowisku. Chociaż Google Glass rozwiązało problem użyteczności, nadal było to nic innego jak obraz 2D wykreślony w powietrzu.

Tytani technologiczni, tacy jak Microsoft, Facebook i Apple, nauczyli się tej trudnej lekcji na pamięć. W czerwcu 2017 r. Apple ogłosiło swoją piękną bibliotekę ARKit na iOS, czyniąc zanurzenie się w niej głównym priorytetem. Trzymanie telefonu nadal jest dużym blokerem doświadczenia użytkownika, ale lekcja Google Glass nauczyła nas, że sprzęt nie jest problemem.

Wierzę, że wkrótce zbliżamy się do nowego szczytu popularności AR, a dzięki temu nowemu znaczącemu zwrotowi może w końcu znaleźć swój rynek macierzysty, pozwalając, by rozwój aplikacji AR stał się głównym nurtem. Oznacza to również, że każda firma zajmująca się tworzeniem aplikacji rozszerzonej rzeczywistości będzie mogła skorzystać z ekosystemu i bazy użytkowników Apple.

Ale dość historii, ubrudźmy sobie ręce kodem i zobaczmy rozszerzoną rzeczywistość Apple w akcji!

Funkcje zanurzenia ARKit

ARKit zapewnia dwie główne funkcje; pierwszy to lokalizacja kamery w przestrzeni 3D, a drugi to wykrywanie płaszczyzny poziomej. Aby osiągnąć to pierwsze, ARKit zakłada, że ​​Twój telefon jest kamerą poruszającą się w rzeczywistej przestrzeni 3D, tak że upuszczenie jakiegoś wirtualnego obiektu 3D w dowolnym punkcie zostanie zakotwiczone w tym punkcie w rzeczywistej przestrzeni 3D. A do tego ostatniego ARKit wykrywa płaszczyzny poziome, takie jak stoły, dzięki czemu można na nich umieszczać obiekty.

Jak więc ARKit to osiąga? Odbywa się to za pomocą techniki zwanej Visual Inertial Odometry (VIO). Nie martw się, tak jak przedsiębiorcy czerpią przyjemność z liczby chichotów, które chichoczesz, gdy dowiadujesz się, skąd kryje się nazwa ich startupu, tak badacze znajdują swoje w liczbie zadrapań, które robisz, próbując rozszyfrować dowolny termin, który wymyślą, gdy nazywanie ich wynalazków – pozwólmy im się więc bawić i ruszać dalej.

VIO to technika łączenia ramek kamer z czujnikami ruchu w celu śledzenia lokalizacji urządzenia w przestrzeni 3D. Śledzenie ruchu z ramek kamery odbywa się poprzez wykrywanie cech lub, innymi słowy, punktów krawędziowych obrazu o wysokim kontraście – takich jak krawędź między niebieskim wazonem a białym stołem. Wykrywając, jak bardzo te punkty przesunęły się względem siebie z jednej klatki do drugiej, można oszacować, gdzie znajduje się urządzenie w przestrzeni 3D. Z tego powodu ARKit nie będzie działał poprawnie, gdy zostanie umieszczony naprzeciwko pozbawionej cech charakterystycznych białej ściany lub gdy urządzenie porusza się bardzo szybko, powodując rozmycie obrazu.

Pierwsze kroki z ARKit w iOS

W momencie pisania tego artykułu ARKit jest częścią iOS 11, który wciąż jest w wersji beta. Aby więc rozpocząć, musisz pobrać iOS 11 Beta na iPhone'a 6s lub nowszy oraz nową wersję Xcode Beta. Możemy rozpocząć nowy projekt ARKit z Nowy > Projekt > Aplikacja Augmented Reality . Jednak uznałem, że wygodniej jest rozpocząć ten samouczek rzeczywistości rozszerzonej od oficjalnej próbki Apple ARKit, która zawiera kilka podstawowych bloków kodu i jest szczególnie pomocna przy wykrywaniu samolotów. Zacznijmy więc od tego przykładowego kodu, najpierw wyjaśnijmy w nim główne punkty, a następnie zmodyfikujmy go dla naszego projektu.

Najpierw powinniśmy określić, jakiego silnika będziemy używać. ARKit może być używany ze Sprite SceneKit lub Metal. W przykładzie Apple ARKit używamy iOS SceneKit, silnika 3D dostarczonego przez Apple. Następnie musimy ustawić widok, który wyrenderuje nasze obiekty 3D. Odbywa się to poprzez dodanie widoku typu ARSCNView .

ARSCNView jest podklasą głównego widoku SceneKit o nazwie SCNView , ale rozszerza widok o kilka przydatnych funkcji. Renderuje obraz na żywo z kamery urządzenia jako tło sceny, a jednocześnie automatycznie dopasowuje przestrzeń SceneKit do rzeczywistego świata, zakładając, że urządzenie jest w tym świecie poruszającą się kamerą.

ARSCNView nie wykonuje samodzielnie przetwarzania AR, ale wymaga obiektu sesji AR, który zarządza kamerą urządzenia i przetwarzaniem ruchu. Tak więc, aby rozpocząć, musimy przypisać nową sesję:

 self.session = ARSession() sceneView.session = session sceneView.delegate = self setupFocusSquare()

Ostatni wiersz powyżej dodaje wizualny wskaźnik, który pomaga użytkownikowi wizualnie opisać stan wykrycia samolotu. Focus Square jest dostarczany przez przykładowy kod, a nie bibliotekę ARKit, i jest to jeden z głównych powodów, dla których zaczęliśmy od tego przykładowego kodu. Więcej na ten temat znajdziesz w pliku readme zawartym w przykładowym kodzie. Poniższy obraz przedstawia rzutowany na stół kwadrat ostrości:

Focus square wyświetlany na stole za pomocą Apple ARKit

Następnym krokiem jest rozpoczęcie sesji ARKit. Ponowne uruchamianie sesji za każdym razem, gdy pojawia się widok, ma sens, ponieważ nie możemy wykorzystać informacji o poprzedniej sesji, jeśli nie śledzimy już użytkownika. Tak więc rozpoczniemy sesję w viewDidAppear:

 override func viewDidAppear(_ animated: Bool) { let configuration = ARWorldTrackingSessionConfiguration() configuration.planeDetection = .horizontal session.run(configuration, options: [.resetTracking, .removeExistingAnchors]) }

W powyższym kodzie zaczynamy od ustawienia konfiguracji sesji ARKit do wykrywania płaszczyzn poziomych. W chwili pisania tego artykułu Apple nie zapewnia innych opcji. Ale najwyraźniej sugeruje to wykrywanie bardziej złożonych obiektów w przyszłości. Następnie rozpoczynamy sesję i upewniamy się, że zresetowaliśmy śledzenie.

Na koniec musimy aktualizować Focus Square za każdym razem, gdy zmienia się pozycja kamery, tj. rzeczywista orientacja lub pozycja urządzenia. Można to zrobić w funkcji delegata renderera w SCNView, która jest wywoływana za każdym razem, gdy ma być renderowana nowa klatka silnika 3D:

 func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) { updateFocusSquare() }

W tym momencie, jeśli uruchomisz aplikację, powinieneś zobaczyć kwadrat ostrości nad strumieniem kamery, szukając płaszczyzny poziomej. W następnej sekcji wyjaśnimy, w jaki sposób wykrywane są samoloty i jak możemy odpowiednio ustawić kwadrat ostrości.

Wykrywanie samolotów w ARKit

ARKit może wykrywać nowe samoloty, aktualizować istniejące lub je usuwać. Aby w wygodny sposób obsługiwać samoloty, utworzymy jakiś fikcyjny węzeł SceneKit, który będzie zawierał informacje o położeniu samolotu i odniesienie do kwadratu ostrości. Płaszczyzny są definiowane w kierunku X i Z, gdzie Y jest normalną powierzchni, tzn. powinniśmy zawsze utrzymywać pozycje naszych węzłów rysunku w obrębie tej samej wartości Y płaszczyzny, jeśli chcemy, aby wyglądała, jakby była wydrukowana na płaszczyźnie .

Wykrywanie samolotów odbywa się za pomocą funkcji zwrotnych dostarczanych przez ARKit. Na przykład po wykryciu nowego samolotu wywoływana jest następująca funkcja zwrotna:

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

Funkcja callback udostępnia nam dwa parametry, anchor i node . node jest normalnym węzłem SceneKit umieszczonym w dokładnej pozycji i orientacji płaszczyzny. Nie ma geometrii, więc jest niewidoczny. Używamy go, aby dodać własny węzeł płaszczyzny, który również jest niewidoczny, ale zawiera informacje o orientacji płaszczyzny i pozycji w anchor .

Jak więc pozycja i orientacja są zapisywane w ARPlaneAnchor ? Pozycja, orientacja i skala są zakodowane w macierzy 4x4. Jeśli będę miał możliwość wybrania dla Ciebie jednej koncepcji matematycznej do nauczenia, to bez wątpienia będą to macierze. W każdym razie możemy to obejść, opisując tę ​​macierz 4x4 w następujący sposób: Genialna dwuwymiarowa tablica zawierająca liczby zmiennoprzecinkowe 4x4. Pomnożenie tych liczb w określony sposób przez wierzchołek 3D, v1 w jego przestrzeni lokalnej, daje w wyniku nowy wierzchołek 3D, v2, który reprezentuje v1 w przestrzeni świata. Tak więc, jeśli v1 = (1, 0, 0) w jego lokalnej przestrzeni i chcemy umieścić go w x = 100 w przestrzeni świata, v2 będzie równe (101, 0, 0) w odniesieniu do przestrzeni świata. Oczywiście, matematyka stojąca za tym staje się bardziej złożona, gdy dodamy obroty wokół osi, ale dobrą wiadomością jest to, że możemy się obejść bez zrozumienia tego (gorąco polecam zajrzeć do odpowiedniej sekcji tego doskonałego artykułu, aby uzyskać dogłębne wyjaśnienie tego pojęcia ).

checkIfObjectShouldMoveOntoPlane sprawdza, czy mamy już narysowane obiekty i sprawdza, czy oś y wszystkich tych obiektów pasuje do nowo wykrytych płaszczyzn.

Wróćmy teraz do updateFocusSquare() , opisanego w poprzedniej sekcji. Chcemy zachować kwadrat ostrości na środku ekranu, ale rzutowany na najbliższą wykrytą płaszczyznę. Poniższy kod pokazuje to:

 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 wyszukuje płaszczyzny świata rzeczywistego odpowiadające punktowi 2D w widoku ekranu, rzutując ten punkt 2D na najbliższą płaszczyznę pod spodem. result.worldTransform to macierz 4x4, która zawiera wszystkie informacje o transformacji wykrytej płaszczyzny, podczas gdy result.worldTransform.translation to przydatna funkcja, która zwraca tylko pozycję.

Teraz mamy wszystkie informacje, których potrzebujemy, aby upuścić obiekt 3D na wykryte powierzchnie, mając punkt 2D na ekranie. Więc zacznijmy rysować.

Rysunek

Wyjaśnijmy najpierw podejście do rysowania kształtów, które podąża za palcem człowieka w wizji komputerowej. Rysowanie kształtów odbywa się poprzez wykrywanie każdej nowej lokalizacji dla poruszającego się palca, upuszczanie wierzchołka w tym miejscu i łączenie każdego wierzchołka z poprzednim. Wierzchołki można połączyć prostą linią lub krzywą Beziera, jeśli potrzebujemy gładkiego wyniku.

Dla uproszczenia przyjrzymy się nieco naiwnemu podejściu do rysowania. Za każde nowe położenie palca upuszczamy bardzo małe pudełko z zaokrąglonymi rogami i prawie zerową wysokością na wykrytym planie. Będzie wyglądał jak kropka. Gdy użytkownik zakończy rysowanie i wybierze przycisk 3D, zmienimy wysokość wszystkich upuszczonych obiektów na podstawie ruchu palca użytkownika.

Poniższy kod przedstawia klasę PointNode , która reprezentuje punkt:

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

Zauważysz w powyższym kodzie, że tłumaczymy geometrię wzdłuż osi y o połowę wysokości. Powodem tego jest upewnienie się, że dno obiektu znajduje się zawsze w punkcie y = 0 , tak aby znajdował się nad płaszczyzną.

Następnie, w funkcji zwrotnej renderera w SceneKit, narysujemy wskaźnik, który działa jak końcówka pióra, używając tej samej klasy PointNode . Opuścimy punkt w tej lokalizacji, jeśli włączony jest rysowanie, lub podniesiemy rysunek do struktury 3D, jeśli włączony jest tryb 3D:

 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 to klasa, która zarządza rysowanymi punktami. W trybie 3D szacujemy różnicę od ostatniej pozycji i zwiększamy/zmniejszamy wysokość wszystkich punktów o tę wartość.

Do tej pory rysujemy na wykrytej powierzchni zakładając, że wirtualne pióro znajduje się na środku ekranu. Teraz zabawna część — wykrywanie palca użytkownika i używanie go zamiast środka ekranu.

Wykrywanie palca użytkownika

Jedną z fajnych bibliotek wprowadzonych przez Apple w iOS 11 jest Vision Framework. Zapewnia kilka technik widzenia komputerowego w bardzo wygodny i wydajny sposób. W szczególności użyjemy techniki śledzenia obiektów w naszym samouczku rzeczywistości rozszerzonej. Śledzenie obiektów działa w następujący sposób: Najpierw dostarczamy obraz i współrzędne kwadratu w granicach obrazu dla obiektu, który chcemy śledzić. Następnie wywołujemy jakąś funkcję inicjującą śledzenie. Na koniec wprowadzamy nowy obraz, w którym zmieniła się pozycja tego obiektu i wynik analizy poprzedniej operacji. W związku z tym zwróci nam nową lokalizację obiektu.

Użyjemy małej sztuczki. Poprosimy użytkownika, aby położył rękę na stole tak, jakby trzymał długopis, i upewnił się, że jego miniatura jest skierowana w stronę kamery, po czym powinien stuknąć miniaturę na ekranie. W tym miejscu należy omówić dwie kwestie. Po pierwsze, miniatura powinna mieć wystarczająco dużo unikalnych cech, aby można ją było prześledzić poprzez kontrast między białą miniaturą, skórą i stołem. Oznacza to, że ciemniejszy pigment skóry zapewni bardziej niezawodne śledzenie. Po drugie, ponieważ użytkownik kładzie ręce na stole, a my już wykrywamy stół jako płaszczyznę, rzutowanie położenia miniatury z widoku 2D na środowisko 3D spowoduje prawie dokładne położenie palca na stół.

Poniższy obraz przedstawia punkty charakterystyczne, które mogą zostać wykryte przez bibliotekę Vision:

Punkty funkcji iOS ARKit wykryte przez bibliotekę Vision

Zainicjujemy śledzenie miniatur w geście stuknięcia w następujący sposób:

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

Najtrudniejszą częścią powyżej jest przekonwertowanie lokalizacji kranu z przestrzeni współrzędnych UIView na przestrzeń współrzędnych obrazu. ARKit dostarcza nam macierz displayTransform , która konwertuje z przestrzeni współrzędnych obrazu do przestrzeni współrzędnych rzutni, ale nie na odwrót. Jak więc możemy zrobić coś odwrotnego? Używając odwrotności macierzy. Naprawdę starałem się zminimalizować użycie matematyki w tym poście, ale czasami jest to nieuniknione w świecie 3D.

Następnie w rendererze wprowadzimy nowy obraz, aby śledzić nową lokalizację palca:

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

Po zakończeniu śledzenia obiektów wywoła funkcję zwrotną, w której zaktualizujemy lokalizację miniatury. Jest to zazwyczaj odwrotność kodu zapisanego w aparacie rozpoznawania tap:

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

Na koniec podczas rysowania użyjemy self.lastFingerWorldPos zamiast środka ekranu i gotowe.

ARKit i przyszłość

W tym poście pokazaliśmy, jak AR może być wciągający poprzez interakcję z palcami użytkowników i prawdziwymi tabelami. Wraz z większymi postępami w wizji komputerowej i dodając do gadżetów więcej sprzętu przyjaznego AR (takich jak kamery głębi), możemy uzyskać dostęp do struktury 3D coraz większej liczby obiektów wokół nas.

Chociaż nie został jeszcze udostępniony masom, warto wspomnieć, że Microsoft bardzo poważnie podchodzi do wygrania wyścigu AR dzięki urządzeniu Hololens, które łączy sprzęt dostosowany do AR z zaawansowanymi technikami rozpoznawania środowiska 3D. Możesz poczekać, aby zobaczyć, kto wygra ten wyścig, lub możesz być jego częścią, tworząc teraz naprawdę wciągające aplikacje rozszerzonej rzeczywistości! Ale proszę, wyświadcz ludzkości przysługę i nie zmieniaj żywych obiektów w króliki.