Ada sedikit orang yang tersisa yang dapat terkejut dengan Augmented Reality (AR). Bagi sebagian orang, teknologi ini dikaitkan dengan mainan selama beberapa jam. Yang lain merasa lebih praktis.
Nama saya Dmitry, dan saya sedang mengembangkan Yandex.Maps untuk iOS. Hari ini saya akan memberi tahu pembaca Habr bagaimana kami membuat rute menggunakan augmented reality. Anda juga akan belajar tentang fitur-fitur menggunakan kerangka kerja ARKit, berkat pengenalan augmented reality tidak lagi menjadi perhatian hanya spesialis di bidang visi komputer.

Pada tahun 2009, majalah Esquire adalah yang pertama di antara media yang menambahkan dukungan augmented reality pada produknya. Di sampul majalah diposting kode yang Anda dapat melihat Robert Downey Jr "hidup".

Penggunaan AR dalam industri hiburan tidak terbatas pada ini. Contoh nyata adalah game Pokemon Go, dirilis pada 2016. Pada bulan Juli tahun itu, itu diunduh lebih dari 16 juta kali. Keberhasilan permainan menyebabkan munculnya banyak klon dengan AR.
Peristiwa penting dalam industri AR dalam beberapa tahun terakhir dapat dianggap sebagai pengumuman Google Glass dan Microsoft Hololens. Munculnya perangkat tersebut menunjukkan vektor di mana perusahaan besar bergerak.
Apple tidak terkecuali. Pada tahun 2017, perusahaan memperkenalkan kerangka kerja ARKit, yang penting bagi industri ini sulit ditaksir terlalu tinggi. Dan kita akan membicarakannya lebih terinci.
ARKit
Fitur ARKit, membuatnya mudah digunakan AR:
- kurangnya kebutuhan akan penanda (penanda) khusus,
- integrasi dengan kerangka kerja grafis 2D / 3D Apple yang ada - SceneKit, SpriteKit, Metal,
- akurasi tinggi dalam menentukan posisi dan orientasi perangkat di ruang,
- tidak perlu mengkalibrasi kamera atau sensor.
Di bawah kap ARKit adalah sistem odometri inersia visual yang menggabungkan data dengan subsistem visual (kamera) dan inersia (akselerometer, giroskop) perangkat untuk menentukan posisi dan perpindahan di atas panggung. Elemen penghubung sistem ini adalah filter Kalman - sebuah algoritma yang pada setiap saat waktu memilih pembacaan terbaik dari dua subsistem dan menyediakannya kepada kami dalam bentuk posisi dan orientasi kami di atas panggung. ARKit juga memiliki "pemahaman" adegan - kita dapat menentukan permukaan horizontal dan vertikal, serta kondisi pencahayaan adegan. Jadi, ketika menambahkan objek ke adegan, kita dapat menambahkan pencahayaan default untuk itu, berkat objek yang akan terlihat lebih realistis.
Ngomong-ngomongSegera, versi kerangka 2.0 akan dirilis, di mana fitur baru akan ditambahkan dan akurasi posisi akan ditingkatkan secara signifikan.
ARKit memungkinkan pengembang untuk menanamkan augmented reality berkualitas tinggi ke dalam aplikasi mereka sambil menghabiskan banyak usaha. Kami akan menunjukkan ini menggunakan contoh Yandex.Maps.
Routing dengan AR di Yandex.Maps
Biasanya, setelah pengumuman versi baru iOS, banyak tim di Yandex berkumpul untuk membahas kemungkinan memperkenalkan fitur baru ke dalam aplikasi mereka. Tim Yandex.Mart melakukan hal yang sama. Dalam waktu satu bulan sejak pengumuman ARKit, kami sering membahas bagaimana mengimplementasikannya di Maps. Gagasan macam apa yang tidak kami dengar dari satu sama lain! Cukup cepat, kami sampai pada kesimpulan bahwa salah satu solusi yang paling berguna dan permukaan adalah penggunaan augmented reality dalam routing.
Pilihan ide ini disebabkan oleh fakta bahwa banyak pengguna kartu sering menghadapi situasi ketika Anda menemukan diri Anda di daerah yang tidak dikenal dan Anda perlu dengan cepat memutuskan ke mana harus pergi. Pendekatan standar untuk pengguna peta rata-rata adalah membuka aplikasi, membangun rute pejalan kaki, dan, setelah berbelok, menentukan ke mana harus bergerak. Gagasan untuk memperkenalkan augmented reality ke dalam rute pejalan kaki adalah untuk menyelamatkan pengguna dari tindakan yang tidak perlu, segera menunjukkan di mana Anda harus bergerak langsung di atas gambar kamera.
Pertama, saya ingin mengatakan beberapa kata tentang perutean. Apa yang saya masukkan dalam konsep ini? Dari sudut pandang implementasi dalam aplikasi seluler, ini adalah serangkaian langkah standar yang memungkinkan pengguna untuk berpindah dari titik A ke titik B:
- pemilihan titik keberangkatan dan kedatangan,
- menerima rute dalam bentuk sekumpulan titik dalam koordinat geografis (lintang, bujur),
- ditampilkan pada peta garis rute,
- menemani pengguna dengan informasi tambahan saat bergerak di sepanjang rute.
Kami tidak akan membahas dua poin pertama. Saya hanya bisa mengatakan bahwa kami mendapatkan rute melalui perpustakaan lintas-platform kami Yandex.Mapkit, yang juga tersedia untuk Anda dalam bentuk pod. Apa perbedaan antara augmented reality dan routing standar pada peta? Pertama-tama, perbedaan utama adalah peta yang hampir sepenuhnya tersembunyi. Penekanan utama ditempatkan pada area layar dengan gambar aliran video dari kamera, di mana elemen visual tambahan ditumpangkan (tanda selesai, tanda tambahan dan gambar garis rute). Setiap elemen visual ini memiliki muatan semantik dan logikanya sendiri (kapan dan bagaimana seharusnya ditampilkan). Kami akan mempertimbangkan peran masing-masing elemen ini secara lebih rinci nanti, tetapi untuk saat ini saya mengusulkan untuk mempertimbangkan tugas-tugas yang kami miliki sebelum kami:
- belajar memposisikan objek pada adegan ARKit, mengetahui koordinat geografisnya,
- pelajari cara menggambar UI yang diperlukan pada adegan 3D dengan kinerja yang memadai.
Kami perlu mengonversi koordinat titik dari geografis ke koordinat di atas panggung, memilih titik mana yang akan ditampilkan, dan menampilkan semua UI yang diperlukan di atas gambar kamera di posisi yang benar. Tapi semuanya ternyata sedikit lebih rumit daripada yang terlihat pada pandangan pertama.
Sebelum mulai mengimplementasikan fitur-fitur secara langsung, salah satu kolega saya diberi tugas membuat prototipe yang menunjukkan kemungkinan (atau ketidakmungkinan) untuk mengimplementasikan fungsionalitas serupa dengan seperangkat alat yang dapat diakses. Selama dua minggu, kami menyaksikan San Sanych membajak ruang terbuka di ruang terbuka dan lingkungan di sekitar kantor kami dengan telepon di tangan dan memandang dunia di sekitar kami melalui prisma kamera. Hasilnya, kami mendapatkan prototipe yang berfungsi yang menunjukkan setiap titik rute sebagai tanda di panggung dengan jarak ke sana. Dengan bantuan prototipe ini dimungkinkan, dengan kombinasi keadaan yang sukses, untuk pergi dari kantor ke metro dan bahkan tidak tersesat. Tapi serius, dia mengkonfirmasi kemungkinan menerapkan fungsi yang dimaksud. Tetapi masih ada beberapa tugas yang masih harus diselesaikan oleh tim kami.
Semuanya dimulai dengan mempelajari alat. Saat itu, hanya satu orang di tim yang memiliki pengalaman bekerja dengan grafik 3D. Mari kita lihat alat-alat yang harus dihadapi siapa pun yang berpikir untuk mengimplementasikan ide-ide semacam itu dengan ARKit.
Alat dan API
Tugas utama rendering objek adalah membuat dan mengelola objek adegan framework SceneKit. Dengan munculnya ARKit, kelas ARSCNView (turunan dari kelas SCNView - kelas dasar untuk bekerja dengan adegan di SceneKit) tersedia untuk pengembang, yang menyelesaikan sebagian besar tugas yang memakan waktu mengintegrasikan ARKit dan SceneKit, yaitu:
- sinkronisasi posisi ponsel di ruang angkasa dengan posisi kamera di atas panggung,
- sistem koordinat adegan bertepatan dengan sistem koordinat ARKit,
- sebagai latar belakang adegan, aliran video dari kamera perangkat digunakan.
Objek ARSCNView juga menyediakan pengembang dengan objek sesi augmented reality yang dapat dimulai dengan konfigurasi yang diperlukan, berhenti, atau berlangganan ke berbagai acara menggunakan objek delegasi.
Untuk menambahkan objek ke adegan, pewaris atau objek SCNNode langsung digunakan. Kelas ini mewakili posisi (vektor tiga dimensi) dalam sistem koordinat induknya. Jadi, kita mendapatkan pohon objek pada adegan dengan root di objek khusus - rootNode adegan kita. Semuanya di sini sangat mirip dengan hierarki objek UIView di UIKit. Objek SCNNode dapat ditampilkan di atas panggung ketika mereka menambahkan material dan pencahayaan.
Untuk menambahkan augmented reality ke aplikasi seluler, Anda juga perlu tahu tentang objek utama API ARKit. Yang utama adalah objek dari sesi augmented reality - ARSession. Objek ini melakukan pemrosesan data dan bertanggung jawab atas siklus hidup dari sesi augmented reality. Tujuan artikel ini bukan untuk menceritakan kembali dokumentasi ARKit dan SceneKit, jadi saya tidak akan menulis tentang semua parameter konfigurasi yang tersedia dari sesi augmented reality, tetapi akan fokus pada salah satu parameter terpenting dari konfigurasi sesi augmented reality untuk aplikasi navigasi - worldAlignment. Parameter ini menentukan arah sumbu adegan pada saat inisialisasi sesi. Secara umum, ketika menginisialisasi sesi augmented reality, ARKit menciptakan sistem koordinat dengan permulaan pada titik yang bertepatan dengan posisi telepon saat ini di ruang angkasa, dan mengarahkan sumbu sistem ini tergantung pada nilai properti Penyelarasan wold. Dalam implementasi kami, nilai gravityAndHeading digunakan, yang menyiratkan bahwa sumbu akan diarahkan sebagai berikut: sumbu Y - dalam arah yang berlawanan dengan gravitasi, sumbu Z - ke selatan, dan sumbu X - ke timur.

Dengan kombinasi keadaan yang baik, sumbu X / Z memang akan disejajarkan dengan arah ke Selatan / Timur, tetapi, karena kesalahan dalam pembacaan kompas, sumbu dapat diarahkan pada sudut tertentu ke arah yang dijelaskan dalam dokumentasi. Ini adalah salah satu masalah yang harus kami tangani, tetapi lebih banyak tentang itu nanti.
Sekarang kita telah memeriksa alat dasar, mari kita rangkum: memetakan rute menggunakan SceneKit menambahkan objek SCNNode ke adegan di posisi yang diperoleh dengan mengonversi dari koordinat geografis ke koordinat pemandangan. Sebelum kita berbicara tentang mengoordinasikan konversi dan umumnya tentang menempatkan objek di tempat kejadian, mari kita bicara tentang masalah rendering elemen UI, dengan asumsi bahwa kita tahu posisi objek di atas panggung.
Tanda selesai
Elemen visual utama dari rute pejalan kaki dengan augmented reality adalah tanda selesai, yang menampilkan titik akhir rute. Juga di atas tanda, kami menunjukkan kepada pengguna jarak ke titik akhir rute.

Ukuran
Ketika kami pertama kali diperlihatkan desain dari tag ini, pertama-tama kami memperhatikan persyaratan untuk ukuran dari tag ini. Mereka tidak mematuhi aturan proyeksi perspektif. Saya akan menjelaskan bahwa dalam mesin tiga dimensi yang digunakan untuk membuat, misalnya, game komputer, "tampilan" dimodelkan menggunakan proyeksi perspektif. Menurut aturan proyeksi perspektif, objek yang jauh digambarkan pada skala yang lebih kecil, dan garis paralel umumnya tidak paralel. Dengan demikian, ukuran proyeksi objek pada bidang layar berubah secara linier (berkurang) saat kamera bergerak menjauh dari objek pada adegan. Ini mengikuti dari deskripsi tata letak bahwa ukuran tanda pada layar memiliki ukuran (maksimum) tetap ketika dihapus kurang dari 50 m, kemudian menurun secara linear dari 50 m menjadi 2 km, setelah itu ukuran minimum tetap tidak berubah. Persyaratan tersebut jelas karena kenyamanan pengguna. Mereka memungkinkan pengguna untuk tidak pernah kehilangan titik akhir rute dari tampilan, sehingga pengguna akan selalu memiliki gagasan tentang ke mana harus pindah.

Kami harus memahami bagaimana kami dapat memasukkan mekanisme proyeksi SceneKit yang bekerja sesuai dengan aturan tertentu. Saya ingin segera mencatat bahwa kami memiliki sekitar dua minggu untuk melakukan segalanya tentang segalanya, jadi tidak ada waktu untuk melakukan analisis mendalam tentang berbagai pendekatan untuk menyelesaikan masalah yang diajukan. Sekarang, menganalisis keputusan kita, mengevaluasinya jauh lebih sederhana, dan kita dapat menyimpulkan bahwa sebagian besar keputusan yang diambil benar. Persyaratan untuk ukuran, pada kenyataannya, adalah batu sandungan pertama. Semua masalah yang diuraikan di bawah ini dapat diselesaikan dengan menggunakan SceneKit dan UIKit. Saya mencoba menjelaskan secara terperinci bagaimana menyelesaikan setiap masalah dengan menggunakan kedua pendekatan tersebut. Pendekatan mana yang digunakan terserah Anda.
Mari kita bayangkan bahwa kami memutuskan untuk menerapkan label akhir menggunakan SceneKit. Jika kita memperhitungkan bahwa label menurut tata letak seharusnya terlihat seperti lingkaran di layar, maka menjadi jelas bahwa dalam SceneKit objek label harus berupa bola (karena proyeksi bola ke pesawat apa pun adalah lingkaran). Agar proyeksi memiliki jari-jari tertentu pada layar, yang ditentukan dalam persyaratan perancang, perlu diketahui jari-jari bola pada setiap saat waktu. Dengan demikian, dengan menempatkan bola dari jari-jari tertentu pada adegan di titik tertentu dan secara konstan memperbarui jari-jarinya ketika mendekati atau bergerak menjauh, kita akan mendapatkan proyeksi ke layar ukuran yang diperlukan setiap saat. Algoritma untuk menentukan jari-jari bola pada titik waktu yang berubah-ubah adalah sebagai berikut:
- menentukan posisi objek di atas panggung - pusat bola,
- temukan proyeksi titik ini di bidang layar (menggunakan API SceneKit),
- untuk menentukan ukuran tanda yang diperlukan pada layar, kami menemukan jarak dari kamera ke pusat bola di panggung,
- kami menentukan ukuran yang diperlukan pada layar dengan jarak ke objek menggunakan aturan yang dijelaskan dalam desain,
- mengetahui ukuran tanda di layar (diameter lingkaran), kami memilih titik pada lingkaran ini,
- membuat proyeksi terbalik (unprojectPoint) dari titik yang dipilih,
- kami menemukan panjang vektor dari titik yang diterima di panggung ke pusat bola.
Nilai yang diperoleh dari panjang vektor akan menjadi jari-jari bola yang diinginkan.

Pada saat implementasi, kami tidak dapat menemukan cara untuk menentukan ukuran objek di tempat kejadian, dan kami memutuskan untuk menggambar tanda selesai menggunakan UIKit. Dalam kasus ini, algoritme mengulangi langkah 1-5, setelah itu lingkaran dengan ukuran yang diinginkan digambar di layar dengan bagian tengah pada titik yang diperoleh pada langkah 2 menggunakan alat UIKit. Contoh implementasi label menggunakan UIKit dapat ditemukan di sini .
Beberapa kata tentang kodePada akhir artikel saya memberikan beberapa tautan ke materi yang bermanfaat dan menarik, termasuk sampel, di mana Anda dapat melihat secara detail kode nyata yang menyelesaikan masalah yang disajikan dalam artikel dan mengimplementasikan algoritma yang disajikan. Minat utama menurut saya adalah prototipe rute pejalan kaki , yang menyatukan semua fungsi, dengan pengecualian mekanisme penyesuaian sumbu, yang dijelaskan secara rinci di bawah ini.
Kode di atas tidak mengklaim optimalitas, kelengkapan dan kualitas produksi =)
Perbedaan antara menggunakan SceneKit dan UIKit dalam kasus ini juga terletak pada kenyataan bahwa ketika menerapkan pada SceneKit, objek SCNNode untuk titik akhir rute (tanda selesai) akan dibuat dengan bahan dan geometri, karena itu harus terlihat, saat menggunakan UIKit kita memerlukan objek node secara eksklusif untuk mencari proyeksi ke bidang layar (untuk menentukan pusat tanda di layar). Dalam hal ini, geometri dan material tidak perlu ditambahkan. Perhatikan bahwa jarak dari kamera ke objek SCNNode dari titik akhir rute dapat ditemukan dalam dua cara - menggunakan koordinat geografis titik-titik, atau sebagai panjang vektor antara titik-titik di tempat kejadian. Ini dimungkinkan karena objek kamera adalah properti SCNNode. Untuk mendapatkan simpul kamera, Anda perlu merujuk ke properti pointOfView dari adegan kami.
Kami belajar cara menentukan jari-jari simpul tanda selesai pada titik arbitrer saat menerapkan pada SceneKit dan posisi tampilan tanda selesai jika diterapkan pada UIKit. Masih mengerti kapan perlu memperbarui nilai-nilai ini? Tempat ini adalah metode objek SCNSceneRendererDelegate:
renderer(_ renderer: SCNSceneRenderer, didRenderScene scene: SCNScene, atTime time: TimeInterval)
Metode ini dipanggil setelah setiap frame adegan yang diberikan. Dengan memperbarui nilai properti di tubuh metode ini, kami mendapatkan label selesai yang ditampilkan dengan benar.
Animasi
Setelah tanda selesai muncul di dev, kami melanjutkan untuk menambahkan animasi riak ke tanda ini. Saya pikir untuk sebagian besar pengembang iOS membuat animasi bukan masalah besar. Tetapi ketika berpikir tentang metode implementasi, kami mengalami masalah dengan terus memperbarui kerangka pandang kami. Perhatikan bahwa dalam kebanyakan kasus, animasi ditambahkan ke objek UIView statis. Masalah serupa - pembaruan konstan jari-jari geometri node muncul ketika diimplementasikan menggunakan SceneKit. Faktanya adalah bahwa animasi berdenyut turun ke animasi ukuran lingkaran (untuk UIKit) dan jari-jari bola (untuk SceneKit). Ya, ya, kita tahu bahwa di UIKit animasi semacam ini dapat dilakukan menggunakan CALayer, tetapi untuk kesederhanaan bercerita, saya memutuskan untuk mempertimbangkan masalah ini secara simetris untuk dua kerangka kerja. Pertimbangkan implementasi di UIKit. Jika Anda menambahkan kode yang menjiwai bingkai yang sama dengan kode yang ada yang memperbarui bingkai tampilan, animasi akan terganggu oleh pengaturan bingkai secara eksplisit. Oleh karena itu, sebagai solusi untuk masalah ini, kami memutuskan untuk menggunakan animasi properti transform.scale.xy dari objek UIView. Saat menerapkan menggunakan SceneKit, Anda harus menambahkan animasi properti skala ke objek SCNNode. Hal yang menyenangkan tentang menggunakan SceneKit dalam kasus ini adalah kenyataan bahwa ia mendukung sepenuhnya CoreAnimation, jadi mempelajari API baru tidak diperlukan. Kode yang mengimplementasikan animasi mirip dengan animasi label di Yandex.Maps terlihat seperti ini:
let animationGroup = CAAnimationGroup.init() animationGroup.duration = 1.0 animationGroup.repeatCount = .infinity let opacityAnimation = CABasicAnimation(keyPath: "opacity") opacityAnimation.fromValue = NSNumber(value: 1.0) opacityAnimation.toValue = NSNumber(value: 0.1) let scaleAnimation = CABasicAnimation(keyPath: "scale") scaleAnimation.fromValue = NSValue(scnVector3: SCNVector3(1.0, 1.0, 1.0)) scaleAnimation.toValue = NSValue(scnVector3: SCNVector3(1.2, 1.2, 1.2)) animationGroup.animations = [opacityAnimation, scaleAnimation] finishNode.addAnimation(animationGroup, forKey: "animations")
Billboard
Di awal artikel, saya menyebutkan papan iklan dengan jarak ke titik akhir rute, yang, pada dasarnya, adalah label dengan teks yang selalu terletak di atas tanda selesai. Secara tradisi, saya akan menguraikan masalah yang melekat pada implementasi pada UIKit dan SceneKit, menceritakan tentang kemungkinan solusi untuk masing-masing kerangka kerja.
Mari kita mulai dengan UIKit. Dalam hal ini, billboard adalah UILabel reguler, di mana teks diperbarui secara konstan, menunjukkan jarak ke titik akhir rute. Mari kita lihat masalah yang kita hadapi.

Jika Anda mengatur label ke bingkai dan kemudian memutar telepon, kita akan melihat bahwa bingkai tidak berubah (akan aneh jika tidak). Pada saat yang sama, kami ingin label tetap sejajar dengan bidang bumi.

Saya pikir semua orang mengerti bahwa ketika mengubah orientasi perangkat, kita perlu mengubah label, tetapi pada sudut apa? Jika Anda menghidupkan imajinasi dan membayangkan semua sumbu sistem koordinat dan vektor yang terlibat dalam proses ini, kita dapat menyimpulkan bahwa sudut rotasi sama dengan sudut antara sumbu x sistem koordinat UIKit dan proyeksi sumbu X sistem koordinat SceneKit pada bidang layar.

Tugas sederhana yang sekali lagi membuktikan kegunaan kursus geometri sekolah.
Saat menerapkan tanda selesai menggunakan SceneKit, kemungkinan besar Anda harus membuat papan iklan dengan jarak menggunakan alat SceneKit, yang berarti bahwa Anda pasti akan memiliki tugas untuk membuat objek SCNNode selalu berorientasi ke arah kamera. Saya pikir masalahnya akan menjadi lebih jelas jika Anda melihat gambar:

Masalah ini diselesaikan dengan menggunakan SCNBillboardConstraint API. Menambahkan konstanta dengan sumbu bebas Y ke kumpulan ruas simpul kami, kami mendapatkan simpul yang berputar di sekitar sumbu Y sistem koordinatnya, sehingga selalu berorientasi ke arah kamera. Satu-satunya tugas pengembang adalah menempatkan simpul ini pada ketinggian yang benar sehingga papan iklan dengan jarak selalu terlihat oleh pengguna.
let billboardConstraint = SCNBillboardConstraint() billboardConstraint.freeAxes = SCNBillboardAxis.Y finishNode.constraints = [billboardConstraint]
Tag Asisten
Salah satu fitur utama perutean pejalan kaki dengan augmented reality, di dalam tim, kami mempertimbangkan tanda bantu - elemen visual khusus yang muncul di layar saat titik akhir rute meninggalkan zona visibilitas dan menunjukkan kepada pengguna tempat membalikkan ponsel sehingga tanda tersebut muncul di layar. garis finish.

Saya yakin banyak pembaca telah menemukan fungsi yang sama di beberapa permainan, paling sering penembak. Betapa mengejutkan tim kami ketika kami melihat elemen UI ini di tata letak. Saya harus segera mengatakan bahwa penerapan fitur yang tepat mungkin memerlukan lebih dari satu jam percobaan dari Anda, tetapi hasil akhirnya sepadan dengan waktu yang dihabiskan. Kami mulai dengan mendefinisikan persyaratan, yaitu:
- untuk setiap orientasi perangkat, label bergerak di sepanjang batas layar,
- jika pengguna telah mengubah 180 derajat ke titik akhir rute, label ditampilkan di bagian bawah layar,
- pada setiap saat waktu, berbelok ke arah tanda harus menjadi belokan terpendek ke titik akhir rute.
Setelah menjelaskan persyaratan, kami mulai implementasi. Hampir segera, kami sampai pada kesimpulan bahwa rendering akan dilakukan menggunakan UIKit. Masalah utama dengan implementasi adalah penentuan pusat label ini pada setiap titik waktu. Setelah mempertimbangkan tanda selesai, tugas seperti itu seharusnya tidak menyebabkan kesulitan, jadi saya tidak akan membahas solusinya secara rinci. Dalam artikel ini saya hanya akan memberikan deskripsi tentang algoritma untuk memilih pusat label bantu, dan kode sumber dapat ditemukan di sini .
Algoritma Pusat Pencarian Algoritma Pencarian:
- membuat objek SCNNode untuk titik akhir rute dengan posisi di tempat kejadian yang diperoleh dari koordinat geografis titik,
- menemukan proyeksi titik pada bidang layar,
- temukan persimpangan segmen dari tengah layar ke titik proyeksi yang ditemukan dengan segmen batas layar dalam sistem koordinat layar.

Titik persimpangan yang ditemukan adalah pusat yang diinginkan dari tanda bantu. Dengan analogi dengan kode yang memperbarui parameter label selesai, kami menempatkan kode yang membuat label tambahan dalam metode delegasi yang sudah disuarakan di atas.
Rute polyline
Setelah membangun rute dan setelah melihat tanda selesai di layar, pengguna dapat mencapainya dengan hanya mengarahkan ke arah tanda, tetapi perutean disebut demikian karena menunjukkan rute ke pengguna. Kami berpikir bahwa akan sangat aneh untuk mengurangi fungsionalitas rute pejalan kaki, tidak termasuk tampilan rute dari versi AR. Untuk memvisualisasikan garis rute, diputuskan untuk menampilkan satu set panah yang bergerak di sepanjang itu. Dalam hal ini, para desainer puas bahwa panah praktis akan hilang ketika pindah (ukuran akan ditentukan oleh aturan proyeksi perspektif), dan diputuskan untuk menggunakan SceneKit untuk implementasi.
Sebelum melanjutkan untuk menggambarkan implementasi, penting untuk dicatat bahwa, secara desain, panah harus berada pada jarak 3 m dari satu sama lain. Jika Anda memperkirakan jumlah objek (panah) yang perlu dirender dengan rute sekitar 1 km, maka itu akan menjadi sekitar 330 buah. Pada saat yang sama, setiap objek ditambahkan animasi gerakan di sepanjang bagian rute. Perhatikan bahwa panah yang berada jauh dari posisi kamera di atas panggung pada jarak sekitar 100-150 meter praktis tidak terlihat karena ukurannya yang kecil. Setelah mempertimbangkan faktor-faktor ini, diputuskan untuk tidak menampilkan semua objek, tetapi untuk menampilkan hanya yang dihapus dari pengguna tidak lebih dari 100 meter di sepanjang garis rute, secara berkala memperbarui set objek yang ditampilkan. Kami menampilkan informasi visual dalam jumlah yang cukup, menghilangkan perhitungan SceneKit yang tidak perlu dan menghemat baterai pengguna.

Mari kita lihat langkah-langkah utama yang harus kita sadari untuk mendapatkan hasil akhir:
- pemilihan bagian rute di mana kami akan menampilkan primitif,
- pembuatan model 3D,
- pembuatan animasi
- perbarui saat mengemudi di sepanjang rute.
Memilih plot untuk ditampilkan
Seperti yang saya catat di atas, kami tidak menampilkan panah untuk seluruh rute, tetapi memilih bagian yang optimal untuk ditampilkan. Pilihan segmen pada titik arbitrer dalam waktu terdiri dalam mencari segmen rute terdekat (rute adalah urutan segmen / segmen) ke posisi saat ini dari pengguna dan memilih segmen dari rute terdekat menuju titik akhir rute sampai total panjangnya melebihi 100 meter.

Pembuatan model 3D
Mari kita pertimbangkan lebih detail proses pembuatan model 3D. Dalam kebanyakan kasus, yang perlu Anda lakukan untuk membuat model 3D sederhana (seperti panah kami) adalah membuka editor 3D apa saja, meluangkan waktu untuk menguasainya, dan membuat model ini di dalamnya. Jika orang-orang dari tim Anda memiliki pengalaman dalam pemodelan 3D, atau mereka punya waktu untuk belajar, misalnya, 3DMax (dan harus dibeli), maka Anda sangat beruntung. Sayangnya, pada saat penerapan fitur ini, tidak ada dari kami yang memiliki pengalaman khusus, tidak ada waktu luang untuk pelatihan, jadi kami harus membuat model, sehingga untuk berbicara, dengan sarana improvisasi. Maksud saya deskripsi model dalam kode. Semuanya dimulai dengan penyajian model 3D dalam bentuk segitiga. Kemudian kita harus secara manual menemukan koordinat simpul segitiga ini dalam sistem koordinat model, dan kemudian membuat array indeks indeks simpul segitiga. Dengan data ini, kami dapat membuat geometri yang diperlukan langsung di SceneKit. Anda dapat membuat model yang mirip dengan kami, misalnya, seperti ini:
class ARSCNArrowGeometry: SCNGeometry { convenience init(material: SCNMaterial) { let vertices: [SCNVector3] = [ SCNVector3Make(-0.02, 0.00, 0.00), // 0 SCNVector3Make(-0.02, 0.50, -0.33), // 1 SCNVector3Make(-0.10, 0.44, -0.50), // 2 SCNVector3Make(-0.22, 0.00, -0.39), // 3 SCNVector3Make(-0.10, -0.44, -0.50), // 4 SCNVector3Make(-0.02, -0.50, -0.33), // 5 SCNVector3Make( 0.02, 0.00, 0.00), // 6 SCNVector3Make( 0.02, 0.50, -0.33), // 7 SCNVector3Make( 0.10, 0.44, -0.50), // 8 SCNVector3Make( 0.22, 0.00, -0.39), // 9 SCNVector3Make( 0.10, -0.44, -0.50), // 10 SCNVector3Make( 0.02, -0.50, -0.33), // 11 ] let sources: [SCNGeometrySource] = [SCNGeometrySource(vertices: vertices)] let indices: [Int32] = [0,3,5, 3,4,5, 1,2,3, 0,1,3, 10,9,11, 6,11,9, 6,9,7, 9,8,7, 6,5,11, 6,0,5, 6,1,0, 6,7,1, 11,5,4, 11,4,10, 9,4,3, 9,10,4, 9,3,2, 9,2,8, 8,2,1, 8,1,7] let geometryElements = [SCNGeometryElement(indices: indices, primitiveType: .triangles)] self.init(sources: sources, elements: geometryElements) self.materials = [material] } } static func arrowBlue() -> SCNGeometry { let material = SCNMaterial() material.diffuse.contents = UIColor.blue material.lightingModel = .constant return ARSCNArrowGeometry(material: material) }
Hasil akhirnya terlihat seperti ini:

Animasi jalur rute
Langkah selanjutnya dalam menampilkan garis animasi rute adalah tahap membuat animasi itu sendiri. Tapi apa cara untuk mewujudkan animasi, yang dalam bentuk akhir terlihat seperti panah mulai bergerak di titik awal bagian yang dipilih dari rute dan "mengapung" di sepanjang rute hingga akhir bagian ini?
Saya tidak akan menjelaskan semua cara yang memungkinkan untuk membuat animasi seperti itu, tetapi saya akan membahas lebih detail tentang metode yang telah kami pilih. Setelah bagian dari rute dipilih, kami membaginya menjadi beberapa bagian dengan panjang yang sama - bagian dari animasi satu panah. Setiap bagian tersebut disorot dalam warna dan memiliki panjang yang sama dengan jarak antara panah.

Di awal setiap bagian, kami membuat objek SCNNode panah, yang animasinya terdiri dari bergerak di sepanjang bagiannya.

Seperti yang Anda lihat, bagian animasi terkadang terdiri dari satu segmen, kadang-kadang dua atau lebih. Itu semua tergantung pada langkah (dalam kasus kami - 3 meter) antara panah dan koordinat titik yang membentuk rute.
Animasi panah adalah urutan dua langkah:
- penampilan di posisi awal dengan sudut awal rotasi,
- urutan offset di sepanjang segmen dengan rotasi di titik koneksi segmen.
Secara skematis, tampilannya seperti ini:

Bagi kami, ini adalah cara termudah untuk mengimplementasikan animasi seperti itu menggunakan SCNAction API - API deklaratif yang memungkinkan Anda untuk membuat animasi berurutan, grup, dan berulang dengan mudah. Anda dapat melihat implementasinya secara lebih rinci di sini . Karena kenyataan bahwa setiap panah mengakhiri animasinya di titik awal dari bagian animasi dari panah berikutnya, kesan pergerakan terus menerus dari panah di sepanjang seluruh bagian rute yang dipilih dibuat.
Mengenai hal ini, saya mengusulkan untuk menyelesaikan pertimbangan berbagai aspek rendering dan pergi ke bagian utama - menentukan posisi objek di atas panggung dengan koordinat geografis objek.
Menentukan posisi suatu objek pada adegan
Kami memulai percakapan tentang menentukan posisi suatu objek di tempat kejadian dengan mempertimbangkan sistem koordinat, konversi di antaranya harus dilakukan. Hanya ada 2 di antaranya:
- koordinat geodesik (atau geografis untuk kesederhanaan) - posisi objek (titik rute) di dunia nyata,
- Koordinat Cartesius - posisi objek di tempat kejadian (dalam ARKit). Ingat bahwa sistem koordinat adegan bertepatan dengan sistem koordinat ARKit (dalam kasus menggunakan ARSCNView).
Terjemahan dari satu sistem koordinat ke yang lain dan sebaliknya dimungkinkan karena fakta bahwa koordinat dalam ARKit diukur dalam meter, dan offset antara dua koordinat geodetik dapat diterjemahkan dengan sangat akurat ke dalam offset dalam meter di sepanjang sumbu X dan Z dari sistem koordinat ARKit pada offset kecil. Biarkan saya mengingatkan Anda bahwa koordinat geodetik adalah titik dengan garis bujur dan garis lintang tertentu.
Mari kita ingat konsep-konsep penting seperti itu dari perjalanan geografi seperti paralel dan garis meridian, dan sifat dasarnya:
- Paralel adalah garis dengan nilai derajat lintang. Panjang dari berbagai persamaan itu berbeda.
- Meridian - garis dengan nilai derajat bujur. Panjang semua meridian adalah sama.
Sekarang mari kita lihat bagaimana Anda dapat menghitung offset dalam meter, antara dua koordinat geodetik dengan koordinat
dan
:
, 
, 
PenjelasanPerpindahan dalam koordinat geodetik memetakan secara linier ke meter hanya pada perpindahan kecil. Pada pemindahan besar, perlu untuk mengambil bagian integral secara jujur.
Sekarang kita dapat menerjemahkan offset dari satu sistem koordinat ke yang lain, kita perlu memutuskan titik referensi - titik di mana koordinat geografis dan koordinat di ARKit (koordinat di atas panggung) diketahui secara bersamaan. Setelah menemukan titik seperti itu, kita dapat menentukan koordinat objek apa pun di atas panggung, mengetahui koordinat geografisnya dan menggunakan rumus di atas.
Untuk kejelasan, pertimbangkan sebuah contoh:
Pada awal sesi augmented reality, kami meminta CoreLocation untuk koordinat geografis kami dan menerimanya langsung -
. Mengingat fakta bahwa asal-usul sistem koordinat ARKit adalah pada awal sesi di titik di mana perangkat berada, kami mendapat titik referensi, karena kami tahu koordinat geografis dan koordinat di tempat kejadian
. Mari kita perlu menemukan koordinat pada adegan objek dengan koordinat geografis
. Untuk melakukan ini, cari offset dalam meter antara koordinat geografis objek dan koordinat geografis dari titik referensi kami, dan kemudian tambahkan offset yang ditemukan ke koordinat di tempat titik referensi. Koordinat yang dihasilkan di tempat kejadian akan menjadi yang diinginkan.

Saya perhatikan bahwa posisi pada adegan yang ditemukan dengan cara ini akan sesuai dengan posisi objek di dunia nyata hanya jika sumbu X / Z dari sistem koordinat adegan disejajarkan dengan arah ke Selatan / Timur. Penyelarasan sumbu, secara teori, harus dicapai dengan mengatur bendera WorldAlignment ke gravitiAndHeading. Tapi seperti yang saya katakan di awal posting, ini jauh dari selalu terjadi.
Mari kita pertimbangkan secara lebih rinci metode penentuan titik referensi. Untuk melakukan ini, kami memperkenalkan konsep perkiraan - seperangkat koordinat geografis dan koordinat di atas panggung.

Metode yang diusulkan di atas untuk menentukan titik referensi mungkin tidak selalu digunakan. Pada saat dimulainya sesi augmented reality, permintaan untuk CLLokasi pengguna mungkin tidak segera dieksekusi, apalagi, akurasi koordinat yang diperoleh mungkin memiliki kesalahan besar. Akan lebih tepat untuk meminta SceneKit untuk posisi di atas panggung saat ini ketika kita mendapatkan nilai dari CoreLocation. Dalam hal ini, komponen estimasi yang dihasilkan memang diperoleh pada saat yang sama, dan kami memiliki kesempatan untuk menggunakan salah satu estimasi tersebut sebagai titik referensi. Saat bekerja dengan ARKit, kesalahan offset terakumulasi dari waktu ke waktu, jadi Apple tidak merekomendasikan menggunakan ARKit sebagai alat navigasi.
Ketika kami memutuskan untuk menerapkan rute pejalan kaki dengan augmented reality, kami melakukan sedikit riset tentang solusi yang ada pada saat itu, menggunakan ARKit untuk tugas serupa, dan menemukan kerangka kerja ARKit + CoreLocation. Gagasan kerangka kerja ini adalah bahwa berkat ARKit kami dapat lebih akurat menentukan lokasi pengguna daripada saat menggunakan secara eksklusif CoreLocation.
Konsep ARKit + CoreLocation:
- saat menerima CLLocation dari CLLocationManager
- meminta posisi di tempat kejadian menggunakan scene.pointOfView.worldPosition
- simpan pasangan koordinat ini (perkiraan) ke buffer
- dapatkan lokasi yang tepat jika perlu
- pilih estimasi terbaik
- menghitung offset antara posisi saat ini di atas panggung dan posisi di atas panggung dari perkiraan terbaik
- terapkan offset yang ditemukan ke koordinat geografis estimasi terbaik
Koordinat geografis pengguna yang dihitung menggunakan estimasi terbaik akan lebih akurat daripada nilai terakhir yang diterima dari CoreLocation, dan pada saat yang sama akan memiliki lebih sedikit kesalahan.
Tetap hanya untuk memahami apa artinya "perkiraan terbaik". Untuk melakukan ini, mari kita lihat bagaimana itu dipilih dan kapan itu diperbarui.
Algoritma untuk memilih estimasi terbaik (dipilih hanya menggunakan komponen geografis):
- terbaik dalam akurasi (memiliki akurasi horizontal terendah),
- yang terbaru di antara perkiraan dengan akurasi yang sama,
- dipilih di antara perkiraan pada jarak tidak lebih dari 100 meter dari lokasi saat ini.
CoreLocation . , , CoreLocation , 100 .
, . , , ( 100 ).
, X/Z ARKit / . ARKit , , .
Mengapa, (, IKEA, ), Y ARKit – , . gravity worldAlignment.
, . , , , . . AR . , , , , . AR.
, . ,
CLLocationManager
—
.
CLLocationManager —
sesuai.
ARKit —
2 CoreLocation
.
. , CoreLocation . . ARKit /.

ARKit Y? . :
- ,
- ,
- ,
- ,
- .
. . CLLocationManager' , ( ), ( ).
1, 2 :
dan
,
– 2, ARKit.
( Bearing).

. , ? , , , , . , , , horizontalAccuracy. , , , . :

, , .
. , . Sebagai contoh:
, , , , (), . , . , , . , , ( ). .
, . , , ( , , ).
Pengujian
, . , , , . 2 :
- , , , , .
. , , 100 CLLocation, . , , , 10 ( 10 ). ? , "". , . , , , . , , . , CoreLocation. , . , .
. , . , (, ), , 0 . , , .
" ". . , , , , CLLocation, , . ( ) .
, ARKit.

, .

( 3-4 ) , .

JS, AR CoreLocation.

— gravity worldAlignment . , . .
Alih-alih sebuah kesimpulan
Slack, , , , . AR. . AR AppStore 2017 . , .
Tautan yang bermanfaat
, . .