Bagaimana GWT Membuka Augmented Reality di Browser Anda
Diterbitkan: 2022-03-11Dalam posting kami sebelumnya di GWT Web Toolkit, kami membahas kekuatan dan karakteristik GWT, yang, untuk mengingat ide umum, memungkinkan kami mentranspile kode sumber Java ke JavaScript dan mencampur perpustakaan Java dan JavaScript dengan mulus. Kami mencatat bahwa JavaScript yang dihasilkan oleh GWT dioptimalkan secara dramatis.
Dalam posting hari ini, kami ingin masuk lebih dalam dan melihat GWT Toolkit beraksi. Kami akan mendemonstrasikan bagaimana kami dapat memanfaatkan GWT untuk membangun aplikasi khusus: aplikasi web augmented reality (AR) yang berjalan secara real time, sepenuhnya dalam JavaScript, di browser.
Dalam artikel ini, kita akan fokus pada bagaimana GWT memberi kita kemampuan untuk berinteraksi dengan mudah dengan banyak API JavaScript, seperti WebRTC dan WebGL, dan memungkinkan kita untuk memanfaatkan perpustakaan Java yang besar, NyARToolkit, yang tidak pernah dimaksudkan untuk digunakan di browser. Kami akan menunjukkan bagaimana GWT mengizinkan tim saya dan saya di Jooink untuk menggabungkan semua bagian ini untuk membuat proyek kesayangan kami, Picshare , aplikasi AR berbasis penanda yang dapat Anda coba di browser Anda sekarang.
Posting ini tidak akan menjadi panduan komprehensif tentang cara membangun aplikasi, melainkan akan menampilkan penggunaan GWT untuk mengatasi tantangan yang tampaknya luar biasa dengan mudah.
Tinjauan Proyek: Dari Realitas ke Augmented Reality
Picshare menggunakan augmented reality berbasis penanda. Jenis aplikasi AR ini mencari tempat untuk penanda : pola geometris yang spesifik dan mudah dikenali, seperti ini. Penanda memberikan informasi tentang posisi dan orientasi objek yang ditandai, memungkinkan perangkat lunak untuk memproyeksikan pemandangan 3D tambahan ke dalam gambar dengan cara yang realistis. Langkah-langkah dasar dalam proses ini adalah:
- Akses Kamera: Saat berhadapan dengan aplikasi desktop asli, sistem operasi menyediakan akses I/O ke banyak perangkat keras perangkat. Ini tidak sama ketika kita berurusan dengan aplikasi web. Peramban dibangun untuk menjadi semacam "kotak pasir" untuk kode JavaScript yang diunduh dari internet, dan awalnya tidak dimaksudkan untuk memungkinkan situs web berinteraksi dengan sebagian besar perangkat keras perangkat. WebRTC menerobos penghalang ini menggunakan fitur pengambilan media HTML5, memungkinkan browser untuk mengakses, antara lain, kamera perangkat dan alirannya.
- Analisis Aliran Video: Kami memiliki aliran video… sekarang apa? Kita harus menganalisis setiap bingkai untuk mendeteksi penanda, dan menghitung posisi penanda di dunia 3D yang direkonstruksi. Tugas kompleks ini adalah urusan NyARToolkit.
- Augment the Video: Terakhir, kami ingin menampilkan video asli dengan menambahkan objek 3D sintetis. Kami menggunakan WebGL untuk menggambar adegan akhir yang ditambah ke halaman web.
Memanfaatkan API HTML5 dengan GWT
Penggunaan API JavaScript seperti WebGL dan WebRTC memungkinkan interaksi yang tidak terduga dan tidak biasa antara browser dan pengguna.
Misalnya, WebGL memungkinkan grafis yang dipercepat perangkat keras dan, dengan bantuan spesifikasi array yang diketik, memungkinkan mesin JavaScript untuk mengeksekusi pemrosesan angka dengan kinerja yang hampir asli. Demikian pula, dengan WebRTC, browser dapat mengakses aliran video (dan data lainnya) langsung dari perangkat keras komputer.
WebGL dan WebRTC keduanya adalah pustaka JavaScript yang harus dibangun ke dalam browser web. Sebagian besar browser HTML5 modern hadir dengan setidaknya sebagian dukungan untuk kedua API (seperti yang Anda lihat di sini dan di sini). Tapi bagaimana kita bisa memanfaatkan alat-alat ini di GWT, yang ditulis dalam Java? Seperti yang dibahas dalam posting sebelumnya, lapisan interoperabilitas GWT, JsInterop (dirilis secara resmi di GWT 2.8) menjadikannya mudah.
Menggunakan JsInterop dengan GWT 2.8 semudah menambahkan -generateJsInteropExports
sebagai argumen ke kompiler. Anotasi yang tersedia didefinisikan dalam paket jsinterop.annotations
, yang dibundel dalam gwt-user.jar
.
WebRTC
Sebagai contoh, dengan pekerjaan pengkodean minimal, menggunakan getUserMedia
WebRTC di Chrome dengan GWT menjadi sesederhana menulis:
Navigator.webkitGetUserMedia( configs, stream -> video.setSrc( URL.createObjectURL(stream) ), e -> Window.alert("Error: " + e) );
Dimana class Navigator
dapat didefinisikan sebagai berikut :
@JsType(namespace = JsPackage.GLOBAL, isNative = true, name="navigator") final static class Navigator { public static native void webkitGetUserMedia( Configs configs, SuccessCallback success, ErrorCallback error); }
Yang menarik adalah definisi antarmuka SuccessCallback
dan ErrorCallback
, keduanya diimplementasikan oleh ekspresi lambda di atas dan didefinisikan dalam Java melalui anotasi @JsFunction
:
@JsFunction public interface SuccessCallback { public void onMediaSuccess(MediaStream stream); } @JsFunction public interface ErrorCallback { public void onError(DomException error); }
Terakhir, definisi URL
kelas hampir identik dengan Navigator
, dan demikian pula, kelas Configs
dapat didefinisikan dengan:
@JsType(namespace = JsPackage.GLOBAL, isNative = true, name="Object") public static class Configs { @JsProperty public native void setVideo(boolean getVideo); }
Implementasi sebenarnya dari semua fungsi ini terjadi di mesin JavaScript browser.
Anda dapat menemukan kode di atas di GitHub di sini.
Dalam contoh ini, demi kesederhanaan, navigator.getUserMedia()
API yang tidak digunakan lagi karena ini adalah satu-satunya yang berfungsi tanpa polyfilling pada rilis stabil Chrome saat ini. Dalam aplikasi produksi, kita dapat menggunakan adapter.js untuk mengakses aliran melalui navigator.mediaDevices.getUserMedia()
API yang lebih baru, secara seragam di semua browser, tetapi ini di luar cakupan diskusi saat ini.
WebGL
Menggunakan WebGL dari GWT tidak jauh berbeda dibandingkan dengan menggunakan WebRTC, tetapi sedikit lebih membosankan karena kompleksitas intrinsik standar OpenGL.
Pendekatan kami di sini mencerminkan yang diikuti di bagian sebelumnya. Hasil pembungkusan dapat dilihat pada implementasi GWT WebGL yang digunakan di Picshare , yang dapat ditemukan di sini, dan contoh hasil yang dihasilkan oleh GWT dapat ditemukan di sini.
Mengaktifkan WebGL sendiri sebenarnya tidak memberi kita kemampuan grafis 3D. Seperti yang ditulis Gregg Tavares:
Yang tidak diketahui banyak orang adalah bahwa WebGL sebenarnya adalah API 2D, bukan API 3D.
Aritmatika 3D harus dilakukan oleh beberapa kode lain, dan ditransformasikan ke gambar 2D untuk WebGL. Ada beberapa pustaka GWT yang bagus untuk grafik 3D WebGL. Favorit saya adalah Parallax, tetapi untuk Picshare versi pertama kami mengikuti jalur yang lebih "lakukan sendiri", menulis perpustakaan kecil untuk merender mesh 3D sederhana. Pustaka memungkinkan kita mendefinisikan kamera perspektif dan mengelola pemandangan objek. Jangan ragu untuk memeriksanya, di sini.
Mengkompilasi Perpustakaan Java Pihak Ketiga dengan GWT
NyARToolkit adalah port Java murni dari ARToolKit, perpustakaan perangkat lunak untuk membangun aplikasi augmented reality. Port ditulis oleh pengembang Jepang di Nyatla. Meskipun ARToolKit asli dan versi Nyatla agak berbeda sejak port asli, NyARToolkit masih aktif dipelihara dan ditingkatkan.
AR berbasis penanda adalah bidang khusus dan membutuhkan kompetensi dalam visi komputer, pemrosesan gambar digital, dan matematika, seperti yang terlihat di sini:
Direproduksi dari dokumentasi ARToolKit.
Direproduksi dari dokumentasi ARToolKit.
Semua algoritme yang digunakan oleh toolkit didokumentasikan dan dipahami dengan baik, tetapi menulis ulang dari awal adalah proses yang panjang dan rawan kesalahan, jadi lebih baik menggunakan toolkit yang sudah ada dan terbukti, seperti ARToolKit. Sayangnya, saat menargetkan web, hal seperti itu tidak tersedia. Toolkit canggih yang paling canggih tidak memiliki implementasi dalam JavaScript, bahasa yang terutama digunakan untuk memanipulasi dokumen dan data HTML. Di sinilah GWT membuktikan kekuatannya yang tak ternilai, memungkinkan kami untuk dengan mudah mentranspile NyARToolkit ke dalam JavaScript, dan menggunakannya dalam aplikasi web dengan sangat sedikit kerumitan.
Kompilasi dengan GWT
Karena proyek GWT pada dasarnya adalah proyek Java, menggunakan NyARToolkit hanyalah masalah mengimpor file sumber di jalur sumber Anda. Namun, perhatikan bahwa karena transpilasi kode GWT ke JavaScript dilakukan pada tingkat kode sumber, Anda memerlukan sumber NyARToolkit, dan bukan hanya JAR dengan kelas yang dikompilasi.
Pustaka yang digunakan oleh Picshare dapat ditemukan di sini. Itu hanya bergantung pada paket yang ditemukan di dalam lib/src
dan lib/src.markersystem
dari build NyARToolkit yang diarsipkan di sini. Kita harus menyalin dan mengimpor paket-paket ini ke dalam proyek GWT kita.

Kita harus memisahkan paket pihak ketiga ini dari implementasi kita sendiri, tetapi untuk melanjutkan dengan “GWT-ization” dari NyARToolkit kita harus menyediakan file konfigurasi XML yang menginformasikan compiler GWT di mana mencari sumber. Pada paket jp.nyatla.nyartoolkit
, kita tambahkan file NyARToolkit.gwt.xml
<module> <source path="core" /> <source path="detector" /> <source path="nyidmarker" /> <source path="processor" /> <source path="psarplaycard" /> <source path="markersystem" /> </module>
Sekarang, dalam paket utama kami, com.jooink.gwt.nyartoolkit
, kami membuat file konfigurasi utama, GWT_NyARToolKit.gwt.xml
, dan menginstruksikan kompiler untuk memasukkan sumber Nyatla di classpath dengan mewarisi dari file XML-nya:
<inherits name='jp.nyatla.nyartoolkit.NyARToolkit'/>
Cukup mudah, sebenarnya. Dalam kebanyakan kasus, ini saja yang diperlukan, tetapi sayangnya kami belum selesai. Jika kami mencoba untuk mengkompilasi atau mengeksekusi melalui Mode Super Dev pada tahap ini, kami menemukan kesalahan yang menyatakan, cukup mengejutkan:
No source code is available for type java.io.InputStream; did you forget to inherit a required module?
Alasan untuk ini adalah bahwa NyARToolkit (yaitu, perpustakaan Java yang ditujukan untuk proyek Java) menggunakan kelas JRE yang tidak didukung oleh Emulated JRE GWT. Kami membahas ini secara singkat di posting sebelumnya.
Dalam hal ini, masalahnya adalah dengan InputStream
dan kelas IO terkait. Seperti yang terjadi, kita bahkan tidak perlu menggunakan sebagian besar kelas-kelas ini, tetapi kita perlu menyediakan beberapa implementasi ke kompiler. Yah, kita bisa menginvestasikan banyak waktu untuk menghapus referensi ini secara manual dari sumber NyARToolkit, tapi itu gila. GWT memberi kami solusi yang lebih baik: Menyediakan implementasi kami sendiri untuk kelas yang tidak didukung melalui tag XML <super-source>
.
<super-source>
Seperti yang dijelaskan dalam dokumentasi resmi:
Tag
<super-source>
menginstruksikan kompiler untuk me-root ulang jalur sumber. Ini berguna untuk kasus di mana Anda ingin menggunakan kembali Java API yang ada untuk proyek GWT, tetapi sumber aslinya tidak tersedia atau tidak dapat diterjemahkan. Alasan umum untuk ini adalah untuk meniru bagian dari JRE yang tidak dilaksanakan oleh GWT.
Jadi <super-source>
adalah yang kami butuhkan.
Kami dapat membuat direktori jre
di proyek GWT, di mana kami dapat menempatkan implementasi kami untuk kelas yang menyebabkan masalah bagi kami:
java.io.FileInputStream java.io.InputStream java.io.InputStreamReader java.io.StreamTokenizer java.lang.reflect.Array java.nio.ByteBuffer java.nio.ByteOrder
Semua ini, kecuali java.lang.reflect.Array
, benar-benar tidak digunakan, jadi kita hanya perlu implementasi bodoh. Misalnya, FileInputStream
kami berbunyi sebagai berikut:
package java.io; import java.io.InputStream; import com.google.gwt.user.client.Window; public class FileInputStream extends InputStream { public FileInputStream(String filename) { Window.alert("WARNING, FileInputStream created with filename: " + filename ); } @Override public int read() { return 0; } }
Pernyataan Window.alert
dalam konstruktor berguna selama pengembangan. Meskipun kita harus dapat mengkompilasi kelas, kita ingin memastikan bahwa kita tidak pernah benar-benar menggunakannya, jadi ini akan mengingatkan kita jika kelas digunakan secara tidak sengaja.
java.lang.reflect.Array
sebenarnya digunakan oleh kode yang kita butuhkan, jadi diperlukan implementasi yang tidak sepenuhnya bodoh. Ini adalah kode kami:
package java.lang.reflect; import jp.nyatla.nyartoolkit.core.labeling.rlelabeling.NyARRleLabelFragmentInfo; import jp.nyatla.nyartoolkit.markersystem.utils.SquareStack; import com.google.gwt.user.client.Window; public class Array { public static <T> Object newInstance(Class<T> c, int n) { if( NyARRleLabelFragmentInfo.class.equals(c)) return new NyARRleLabelFragmentInfo[n]; else if(SquareStack.Item.class.equals(c)) return new SquareStack.Item[n]; else Window.alert("Creating array of size " + n + " of " + c.toString()); return null; } }
Sekarang jika kita menempatkan <super-source path="jre"/>
di file modul GWT_NyARToolkit.gwt.xml
, kita dapat dengan aman mengkompilasi dan menggunakan NyARToolkit dalam proyek kita!
Merekatkan Semuanya Bersama GWT
Sekarang kita berada dalam posisi memiliki:
- WebRTC, sebuah teknologi yang mampu mengambil streaming dari webcam dan menampilkannya dalam tag
<video>
. - WebGL, sebuah teknologi yang mampu memanipulasi grafik yang dipercepat perangkat keras dalam
<canvas>
HTML. - NyARToolkit, perpustakaan Java yang mampu mengambil gambar (sebagai array piksel), mencari penanda dan, jika ditemukan, memberi kita matriks transformasi yang sepenuhnya mendefinisikan posisi penanda dalam ruang 3D.
Tantangannya sekarang adalah mengintegrasikan semua teknologi ini bersama-sama.
Kami tidak akan membahas secara mendalam tentang cara mencapai ini, tetapi ide dasarnya adalah menggunakan citra video sebagai latar belakang pemandangan kami (tekstur yang diterapkan pada bidang "jauh" pada gambar di atas) dan membangun struktur data 3D memungkinkan kami untuk memproyeksikan gambar ini ke luar angkasa menggunakan hasil dari NyARToolkit.
Konstruksi ini memberi kita struktur yang tepat untuk berinteraksi dengan perpustakaan NyARToolkit untuk pengenalan penanda, dan menggambar model 3D di atas pemandangan kamera.
Membuat aliran kamera dapat digunakan agak rumit. Data video hanya dapat digambar ke elemen <video>
. Elemen HTML5 <video>
buram, dan tidak memungkinkan kami mengekstrak data gambar secara langsung, jadi kami terpaksa menyalin video ke <canvas>
perantara, mengekstrak data gambar, mengubahnya menjadi array piksel, dan akhirnya dorong ke metode Sensor.update()
. Kemudian NyARToolkit dapat melakukan pekerjaan mengidentifikasi penanda pada gambar, dan mengembalikan matriks transformasi yang sesuai dengan posisinya di ruang 3D kita.
Dengan elemen ini, kita dapat menempatkan objek sintetis tepat di atas penanda, dalam 3D, dalam aliran video langsung! Berkat kinerja tinggi GWT, kami memiliki banyak sumber daya komputasi, sehingga kami bahkan dapat menerapkan beberapa efek video pada kanvas, seperti sepia atau blur, sebelum menggunakannya sebagai latar belakang untuk adegan WebGL.
Kode singkat berikut menjelaskan inti dari proses:
// given a <canvas> drawing context with appropriate width and height // and a <video> where the mediastream is drawn ... // for each video frame // draw the video frame on the canvas ctx.drawImage(video, 0, 0, w, h); // extract image data from the canvas ImageData capt = ctx.getImageData(0, 0, w, h); // convert the image data in a format acceptable by NyARToolkit ImageDataRaster input = new ImageDataRaster(capt); // push the image in to a NyARSensor sensor.update(input); // update the NyARMarkerSystem with the sensor nyar.update(sensor); // the NyARMarkerSystem contains information about the marker patterns and is able to detect them. // After the call to update, all the markers are detected and we can get information for each // marker that was found. if( nyar.isExistMarker( marker_id ) ) { NyARDoubleMatrix44 m = nyar.getMarkerMatrix(marker_id); // m is now the matrix representing the pose (position and orientation) of // the marker in the scene, so we can use it to superimpose an object of // our choice ... } ...
Dengan teknik ini, kita dapat menghasilkan hasil seperti ini:
Ini adalah proses yang kami gunakan untuk membuat Picshare , di mana Anda diundang untuk mencetak penanda atau menampilkannya di ponsel Anda, dan bermain dengan AR berbasis penanda di browser Anda. Menikmati!
Catatan Akhir
Picshare adalah proyek peliharaan jangka panjang bagi kami di Jooink. Implementasi pertama dimulai beberapa tahun yang lalu, dan bahkan saat itu cukup cepat untuk menjadi mengesankan. Di tautan ini Anda dapat melihat salah satu eksperimen kami sebelumnya, yang disusun pada tahun 2012 dan tidak pernah disentuh. Perhatikan bahwa dalam sampel hanya ada satu <video>
. Dua jendela lainnya adalah elemen <canvas>
yang menampilkan hasil pemrosesan.
GWT cukup kuat bahkan pada tahun 2012. Dengan dirilisnya GWT 2.8, kami telah memperoleh lapisan interoperabilitas yang jauh lebih baik dengan JsInterop, yang semakin meningkatkan kinerja. Selain itu, untuk merayakan banyak orang, kami juga memperoleh lingkungan pengembangan dan debugging yang jauh lebih baik, Mode Super Dev. Oh ya, dan dukungan Java 8.
Kami menantikan GWT 3.0!