Rozszerzona rzeczywistość na iOS (i nie tylko) – perspektywy i możliwości

Będąc zaledwie brzdącem, stałem się fanem filmów science-fiction. Do grupy tych, które lubiłem najbardziej zaliczały się „Gwiezdne Wojny”, „Łowca androidów”, „Powrót do przyszłości” i wiele, wiele innych. To, co najbardziej mnie pociągało w nich wszystkich, to nie romantyczne opowieści o tym co dobre i złe (nudy), lecz fantastyczne wizje reżyserów, scenarzystów i autorów tych utworów filmowych. Rozpalali oni wyobraźnie o nowej, lepszej przyszłości. W niej świat był pełen cudów techniki, które rozszerzały możliwości człowieka do wręcz magicznych granic. Rozmowy na odległość z najbardziej odległego miejsca w przestrzeni, przesyłanie obrazów na żywo, wyświetlanie hologramów trójwymiarowych map planet były czymś naturalnym i niewymagającym dla bohaterów. Wystarczyło, że wyciągnęli z kieszeni „nadajnik”, poprosili bazę o konkretne informację i już. Niestety w roku 1995 niemożliwe było, abym zabrał ze sobą komputer ani tym bardziej schował go do kieszeni moich króciutkich spodni sztruksowych. Na szczęście (albo i nie) te czasy są już daleko za nami i każdy ma w kieszeni swój prywatny „nadajnik”, który z każdym rokiem zyskuje nowe możliwości. Mapy całego globu, rozmowy na odległość, dostęp do globalnej sieci danych, to wszystko jest dla Ciebie dostępne – wystarczy spojrzenie na Twój smartphone. Obserwując ten rozwój techniczny byłem bardzo podekscytowany, ponieważ „rozszerzona rzeczywistość” nie jest już tylko akademickim sloganem, enigmatem zrozumiałym tylko dla wybranych, lecz staje się faktem.

Czym jest rozszerzona rzeczywistość?

Rzeczywistością rozszerzoną (Augmented Reality – w skrócie AR) nazywamy świat, w którym realne elementy są wzbogacane przez treści generowane komputerowo. Zazwyczaj ludzie myślą o obrazie z kamer, na który nakładane są elementy trójwymiarowe, w praktyce nakładana może być również warstwa dźwiękowa. Połączenie tych elementów otwiera wiele nowych możliwości dla inżynierów XXI wieku. W związku z tym AR przestaje być tylko narzędziem rozrywki, staje się rozwiązaniem przydatnym również w innych dziedzinach takich jak np. edukacja, nawigacja czy medycyna. Przykładów na aplikacje jest wiele, mnie osobiście spodobała się aplikacja „Measure”, która z całkiem niezłą dokładnością mierzy odległości i powierzchnie. Przykładem globalnego sukcesu jest gra „Pokemon GO”, która polega na szukaniu cyfrowych stworzeń. Cały mechanizm gry opiera się o rozszerzoną rzeczywistość. Planszą w grze jest nasz świat, a mapa na urządzeniu wskazuje, w którym miejscu mogą znajdować się „Pokemony”. Gracz po dotarciu na miejsce może zobaczyć na ekranie swojego urządzenia wirtualne stworzenie i podjąć próbę schwytania go do swojej kolekcji.

Pokemon Go

Przykładem, w którym nawigacja jest rozszerzona przez AR, może być aplikacja Google Maps. Korzystając z trybu LiveView na ekranie naszego urządzenia, po uprzednim zeskanowaniu otoczenia, będą wyświetlane wskazówki dotarcia do wybranego przez nas miejsca. Tego typu aplikacje są cały czas udoskonalane i oprócz drogowskazów mogą wyświetlać nam również cyfrowe banery mijanych kawiarni, barów, kin etc.

Niestety jedną z wad obecnych rozwiązań jest sposób korzystania z niego. Użytkownik musi trzymać ekran smartfona przed swoimi oczami, co na dłuższą metę może być bardzo niewygodne. Na nasze szczęście inżynierowie gigantów branży cyfrowej pomyśleli i o tym – od kilku lat zintensyfikowali prace nad rozwiązaniem, które ma tę niedogodność usunąć. Mam na myśli oczywiście okulary z wyświetlaczem cyfrowym, które są tworzone przez wiele różnych firm, między innymi Apple, Google. Do doskonałości zwłaszcza w kontekście wyglądu jeszcze im nieco brakuje. Jestem jednak przekonany, że z czasem okulary obecnie przypominające skomplikowaną wojskową technologię do szpiegowania zamkniętych pomieszczeń będą coraz bardziej przypominały te, z których korzystamy na co dzień.

Okulary pokazujące rozszerzoną rzeczywistość.

Jak działa i co oferuje nam ARKit na obecną chwilę?

Być może zaskakującym będzie dla Ciebie fakt, drogi Czytelniku, jakie elementy składają się na bibliotekę ARKit. Są to między innymi biblioteka AVFoundation, która służy do wyświetlania, nagrywania i odtwarzania obrazów i dźwięków na Twoim iPhonie. Kolejna z nich to CoreMotion, której zadaniem jest monitorowanie obrotu urządzenia każdej z osi oraz wykrywanie jego przyspieszenia. Ostatnią, która ma ogromne znaczenie jest CoreML (Core Machine Learning), która wykorzystuje uczenie maszynowe do rozpoznawania przedmiotów, obrazów oraz spaja informacje z wcześniej wymienionych bibliotek na zestaw danych potrzebnych do prezentacji AR. Jeżeli aktywnie korzystasz ze swojego smartfona, to doskonale wiesz, że wykrywanie twarzy nie powinno stanowić dla ARKit żadnego problemu.

Kolejnym elementem mogą być ograniczone powierzchnie, zwane też „obrazami”. Dają nam one możliwość stworzenia wirtualnych okien i wyświetlaczy dla zasilenia naszej rozszerzonej rzeczywistości nowymi widokami. Innym typem powierzchni mogą być te bardziej bazowe, jak podłoga czy ściana, które możemy adaptować na grunt i granice naszego wirtualnego świata. Ostatnia bardzo przydatna rzecz to wykrywanie własnych obiektów. Warunkiem jest posiadanie referencji tego obiektu w swojej bazie danych. Możemy takie obiekty zeskanować przy użyciu skanera i dodać go do aplikacji.
W wykorzystaniu tych wszystkich elementów ogranicza nas jedynie nasza wyobraźnia – pojawiły się już aplikacje, które zmieniają ludzką twarz w karykaturalną wersją i pozwalają latać zdalnie sterowanym samolotem po pokoju.

Warto wspomnieć też, że do dyspozycji programistów iOS jest kilka aplikacji, które ułatwiają tworzenie AR, takich jak np. „Reality Composer” w którym istnieje możliwość stworzenia „filtrów twarzy”, czy aplikacja „Create ML” dzięki której stworzymy model do wykrywania wybranych obiektów.

Rozszerzona rzeczywistość – jak stworzyć ją samemu?

No dobrze, drogi Czytelniku, ustaliliśmy już, że możemy tworzyć nowy, lepszy świat. Co mamy do zaoferowania? Jako zadowolony posiadacz kota, zdaje sobie sprawę, że włochata właściwość kota, może być dla alergików przeszkodą w czerpaniu przyjemności z zabawy z tym stworzeniem. Cyfrowy pupil może być odpowiedzią na tę bolączkę, dlatego napiszemy podwaliny pod słynną grę „Tamagochi” i stworzymy wirtualny świat dla dowolnego zwierzęcia. Do tego celu użyjemy ARKit oraz SceneKit, czyli jednego ze sposobów tworzenia scen 3D na iOS. Na naszej scenie ustawimy powierzchnię oraz umieścimy wirtualne zwierzę.

Rozpoczynając lekturę tego krótkiego poradnika, rozpocznij od stworzenia w XCode nowego projektu z pustym ekranem. Przejdź do automatycznie stworzonego kontrolera widoku „ViewController” i zaimportuj do niego biblioteki ARKit, SceneKit oraz UIKit, jeżeli jeszcze jej tam nie ma. Dodatkowo wprowadź dla ViewController dziedziczenie po ARSCNViewDelegate. To bardzo ważne, ponieważ z tym delegatem będziemy cały czas współpracować.

import UIKit
import ARKit
import SceneKit
​
class ViewController: UIViewController, ARSCNViewDelegate { 
// ... pozostała część kontrolera

Teraz przejdź do funkcji viewDidLoad() i przepisz poniższy kod.

// 0
var sceneView: ARSCNView?
var planeAnchor: ARPlaneAnchor?
    
override func viewDidLoad() {
   super.viewDidLoad()
        
   // 1
   sceneView = ARSCNView(frame: self.view.bounds)
   sceneView?.delegate = self
   sceneView?.debugOptions = ARSCNDebugOptions.showFeaturePoints
        
   // 2
   view.addSubview(sceneView!)
        
   // 3
   let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(spawnChicken(_:)))
   sceneView?.addGestureRecognizer(tapGestureRecognizer)
        
   // 4
   let configuration = ARWorldTrackingConfiguration()
   configuration.planeDetection = .horizontal
   sceneView?.session.run(configuration)
}

Rozszerzona rzeczywistość – projektowanie krok po kroku

W punkcie przy komentarzu „0” dodajemy dwie referencje. Jedna to ARSCNView, czyli nasz widok sceny rozszerzonej rzeczywistości. Druga to ARPlaneAnchor, czyli referencja do naszej płaszczyzny na scenę. Zauważ, że jest ona optional ze względu na fakt, że płaszczyzna po uruchomieniu sesji AR nie jest od początku znana, dopiero musi zostać przez nas wykryta.

Krok „1” pokazuje jak łatwo i szybko tworzy się scenę AR. Chcemy, aby miała ona rozmiary naszego widoku, wskazujemy delegata jako nasz obecny „ViewController” i na koniec dodajemy opcję debugowania, która pozwoli nam w trakcie wyszukiwania sceny wyświetlać pomocnicze punkty na ekranie urządzenia. To byłoby na tyle jeśli chodzi o zainicjalizowanie rozszerzonej rzeczywistości. Nie ma tam skomplikowanych parametrów
i ustawień, nie musimy przejmować się inicjowaniem kamery, jest to już zrobione za nas.

Punkt „2” to dodanie sceny rozszerzonej na nasz widok. Pamiętaj, że konstrukcja force (z wykrzyknikiem) nie jest zalecana, dlatego korzystaj z niej tylko kiedy jesteś pewny/a, że nie wywoła to odwołania do pustego miejsca w pamięci! 

Krok numer „3” to dodanie funkcji rozpoznawania uderzenia w ekran. Przyda się ona nam do umieszczania naszych zwierzątek na scenie. Na ten moment możesz ją umieścić w komentarzu.

Ostatni krok, czyli „4”, to ustawienie konfiguracji śledzenia. W naszym przypadku ustawiliśmy wykrywanie świata i powierzchni poziomych, a następnie rozpoczęliśmy sesję AR z tą konfiguracją. Inne konfiguracje mogą dotyczyć twarzy, obiektów lub obrazów.

override func viewWillDisappear(_ animated: Bool) {
   super.viewWillDisappear(animated)
        
   // 5
   sceneView?.session.pause()
}

Dobrą praktyką jest zatrzymywanie wszelkich sesji (jeżeli jej działanie nie jest wymagane) w momencie, kiedy aplikacja wchodzi w tło lub dany ekran znika, dlatego w punkcie „5” nasza sesja zostaje zapauzowana. Po powrocie możemy uruchomić w funkcji viewWillAppear() tak jak to zrobiliśmy
w punkcie „4”.

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
        
   // 6
   guard let planeAnchor = anchor as? ARPlaneAnchor else { 
      return 
   }
   guard self.planeAnchor == nil else { 
      return 
   }
        
   // 7
   self.planeAnchor = planeAnchor
        
   // 8
   let width = CGFloat(1.5)
   let plane = SCNPlane(width: width, height: width)
        
   // 9
   plane.materials.first?.diffuse.contents = UIImage(named: "Grass")
        
   // 10
   let planeNode = SCNNode(geometry: plane)
   planeNode.position = SCNVector3(0, 0, 0)
   planeNode.eulerAngles.x = -.pi / 2.0
   planeNode.physicsBody = SCNPhysicsBody(type: .kinematic, shape: .init(geometry: plane, options: nil))
        
   // 11
   let lightNode = SCNNode()
   lightNode.light = SCNLight()
   lightNode.light?.type = .omni
   lightNode.position = SCNVector3(0, 2, 0)
   lightNode.light?.intensity = 2000
        
   // 12
   node.addChildNode(lightNode)
   node.addChildNode(planeNode)
   sceneView?.debugOptions = SCNDebugOptions()
}

Przyszedł czas na funkcję delegata „renderer did add node for anchor”.
W tej funkcji, jeżeli zostanie znaleziony obiekt odpowiadający naszym preferencjom, będziemy poinformowani o tym zdarzeniu. 

Krok „6”, to sprawdzenie czy znaleziony obiekt jest typu ARPlaneAnchor, czyli naszą powierzchnią płaską, która jest wymagana przez konfigurację. Ponieważ sesja śledzenia świata nie może zostać zatrzymana, a cały czas będzie wyszukiwać nowe fragmenty powierzchni, stworzony został prosty mechanizm gwarantujący dodanie tylko jednego z nich. Referencja pierwszej znalezionej powierzchni zostanie zachowana w kontrolerze widoku (punkt „7”), a konstrukcja „guard” zabroni tworzenia kolejnych płaszczyzn (punkt „6”).

Następnie punkt „8” to ustalenie boku naszej powierzchni, na wartość CGFloat równą 1,5. Będzie to odpowiadało wielkości 1,5 metra w rzeczywistości. Warto
w tym miejscu zaznaczyć, że SceneKit posługuje się jednostkami metrycznymi, dlatego projektując świat AR warto otworzyć stary zeszyt do fizyki. Ostatnim punktem przy tworzeniu geometrii płaszczyzny, czyli numer „9”, jest dodanie grafiki, którą wcześniej przygotowałem w projekcie. 

Przechodząc do punktu „10”, chciałbym poinformować o następującym fakcie – wszystkie elementy sceny są tak zwanymi „węzłami” i nasza rola to określenie czym te węzły będą. Dlatego wcześniejszy obiekt SCNPlane nie był jeszcze pełnoprawnym członkiem naszej wirtualnej sceny i aby zaistniał, musi zostać zainicjalizowany jak SCNNode. W kolejnych linijkach ustawiamy jest jego pozycję, obrót oraz nadajemy mu pewne fizyczne właściwości SCNPhysicsBody. Typ „kinematic” oznacza, że wszystkie ciała fizyczne będą z nim kolidować, jednak w przeciwieństwie do „dynamic” nie będą go poruszać. Czyli nasz węzeł będzie zachowywał się jak podłoga, cokolwiek na nią spadnie, nie zmieni jej położenia ani jej nie odkształci.

Przedostatni punkt „11”, to stworzenie światła, które umieścimy fikcyjne 2 metry nad naszą sceną. Możemy oczywiście ocenić jasność naszej sceny, poprzez określanie jasności obrazu z kamery, ale na nasze potrzeby ustawmy wartość „intensity” na 2000. Typ światła „omni” oznacza światło rozchodzące się w każdym kierunku, tak jak światło z nieboskłonu. Punkt „12” to już tylko dodanie naszych stworzonych „węzłów-dzieci” dla „węzła-scena” oraz wyłączenie punktów pomocniczych. Po uruchomieniu aplikacji skieruj kamerę urządzenia w stronę podłogi, ruszaj nim powoli i obracaj.
W ten sposób ARKit będzie wynajdywać punkty na podłodze oraz oceni rozmiary i odległości. Kiedy ARKit wykryje podłogę, automatycznie zostanie ona dodana do sceny.

AR -projektowanie w praktyce

Ostatni krok w naszym projekcie to dodanie zwierzątka. Mam już przygotowaną scenę z nim w formacie .scn, w XCode jest dostępna też przykładowa scena ship.scn. Przekopiuj poniższy fragment kodu:

@objc func spawnChicken(_ recognizer: UIGestureRecognizer) {
   guard let pos = self.planeAnchor?.center else { 
      return 
   }
        
   // 13
   DispatchQueue.global(qos: .background).async {
            
      // 14
      let chickenScene = SCNScene(named: "art.scnassets/chick.scn")!
      chickenScene.rootNode.physicsBody = SCNPhysicsBody(type: .dynamic, shape: SCNPhysicsShape())
      chickenScene.rootNode.position = SCNVector3(pos.x, pos.y, pos.z)
            
      // 15
      self.sceneView?.scene.rootNode.addChildNode(chickenScene.rootNode)
   }
}

Punkt „13” otwiera nam nowy wątek w background. To ważne, aby dodanie naszego zwierzęcia odbyło się „w tle”.

rozszerzona rzeczywistość

Kolejno w punkcie „14” inicjalizujemy wcześniej przygotowaną scenę z wybranym przez nas zwierzęciem oraz nadajemy mu fizyczne właściwości. Na ten moment wystarczy pusty kształt SCNPhysicsShape oraz typ ciała „dynamic”. Przez to, że nasz „node” będzie dynamiczny, będzie podlegał prawom fizyki naszego rozszerzonego świata i będzie działać na niego grawitacja. Ustalamy mu też pozycję na środku płaszczyzny. Punkt „15” to tylko dodanie naszego „węzła-kury” do „węzła-sceny”. Uruchom aplikację
i powtórz proces wyszukiwania powierzchni, lecz w momencie, w którym zostanie znaleziona podłoga, „tapnij” w ekran smartfona. 

Voilà! Oto nasz cyfrowy pupil. Z tego co wiem, to oczekuje na dopisanie funkcji karmiących i opiekujących. To już czytelniku pozostawiam Twojej inwencji :)

Zakończenie

Podsumowując świat z każdym dniem zmierza coraz bardziej ku cyfryzacji i wydaje się, że tylko kataklizm jest w stanie zatrzymać ten proces. Codziennie setki pomysłów na innowacje stawiają podwaliny pod nowy, nieznany kierunek rozwoju i to co dawniej było ekstrawaganckim luksusem, dzisiaj jest co najwyżej ciekawym dodatkiem. 

Mam nadzieję, że pokazałem Ci jak prosta jest biblioteka ARKit oraz jak niewielkim nakładem sił można stworzyć bardzo prostą scenę. Coraz więcej aplikacji będzie korzystać z rozwiązań sztucznej inteligencji oraz rozszerzonej rzeczywistości, dlatego mam nadzieję, że niedługo będziemy świadkami coraz to nowszych i ciekawszych aplikacji, które umilą, lub usprawnią naszą egzystencję.

Sprawdź koniecznie artykuł o wykorzystaniu wykorzystaniu IoT.

Udostępnij
Autor:  Michał Krupa
Programista aplikacji mobilnych na iOS. Hobbysta gier komputerowych oraz sportów ekstremalnych.