كيف تفتح GWT الواقع المعزز في متصفحك
نشرت: 2022-03-11في مقالنا السابق على GWT Web Toolkit ، ناقشنا نقاط قوة وخصائص GWT ، والتي ، لتذكر الفكرة العامة ، تتيح لنا تحويل شفرة مصدر Java إلى JavaScript ودمج مكتبات Java و JavaScript بسلاسة. لاحظنا أن JavaScript الذي تم إنشاؤه بواسطة GWT تم تحسينه بشكل كبير.
في منشور اليوم ، نود أن نتعمق قليلاً ونرى مجموعة أدوات GWT قيد التنفيذ. سنشرح كيف يمكننا الاستفادة من GWT لبناء تطبيق خاص: تطبيق ويب للواقع المعزز (AR) يعمل في الوقت الفعلي ، بالكامل في JavaScript ، في المتصفح.
في هذه المقالة ، سوف نركز على كيف يمنحنا GWT القدرة على التفاعل بسهولة مع العديد من واجهات برمجة تطبيقات JavaScript ، مثل WebRTC و WebGL ، ويسمح لنا بتسخير مكتبة Java كبيرة ، NyARToolkit ، لم يقصد استخدامها مطلقًا في المتصفح. سنوضح كيف سمحت GWT لفريقي وأنا في Jooink بوضع كل هذه القطع معًا لإنشاء مشروع الحيوانات الأليفة ، Picshare ، تطبيق AR قائم على العلامات يمكنك تجربته في متصفحك الآن.
لن يكون هذا المنشور تجولًا شاملاً حول كيفية إنشاء التطبيق ، ولكنه سيعرض استخدام GWT للتغلب على التحديات التي تبدو ساحقة بسهولة.
نظرة عامة على المشروع: من الواقع إلى الواقع المعزز
يستخدم Picshare الواقع المعزز المستند إلى العلامات. يبحث هذا النوع من تطبيقات AR في المشهد عن علامة : نمط هندسي محدد يسهل التعرف عليه ، مثل هذا. توفر العلامة معلومات حول موضع الكائن المحدد واتجاهه ، مما يسمح للبرنامج بعرض مشهد ثلاثي الأبعاد إضافي في الصورة بطريقة واقعية. الخطوات الأساسية في هذه العملية هي:
- الوصول إلى الكاميرا: عند التعامل مع تطبيقات سطح المكتب الأصلية ، يوفر نظام التشغيل إمكانية الوصول إلى الإدخال / الإخراج إلى الكثير من أجهزة الجهاز. الأمر يختلف عندما نتعامل مع تطبيقات الويب. تم تصميم المتصفحات لتكون بمثابة "صندوق حماية" لرمز جافا سكريبت الذي يتم تنزيله من الشبكة ، ولم يكن الغرض منها في الأصل السماح لمواقع الويب بالتفاعل مع معظم أجهزة الجهاز. يخترق WebRTC هذا الحاجز باستخدام ميزات التقاط وسائط HTML5 ، مما يمكّن المتصفح من الوصول ، من بين أشياء أخرى ، إلى كاميرا الجهاز ودفقه.
- تحليل دفق الفيديو: لدينا دفق الفيديو ... ماذا الآن؟ علينا تحليل كل إطار لاكتشاف العلامات ، وحساب موضع العلامة في العالم ثلاثي الأبعاد المعاد بناؤه. هذه المهمة المعقدة هي عمل NyARToolkit.
- تعزيز الفيديو: أخيرًا ، نريد عرض الفيديو الأصلي مع إضافة كائنات ثلاثية الأبعاد اصطناعية. نستخدم WebGL لرسم المشهد النهائي الموسع على صفحة الويب.
الاستفادة من واجهات برمجة تطبيقات HTML5 مع GWT
يتيح استخدام واجهات برمجة تطبيقات JavaScript API مثل WebGL و WebRTC تفاعلات غير متوقعة وغير عادية بين المتصفح والمستخدم.
على سبيل المثال ، يسمح WebGL بالرسومات المسرَّعة للأجهزة ، وبمساعدة مواصفات الصفيف المكتوبة ، يُمكِّن محرك JavaScript من تنفيذ معالجة الأرقام بأداء أصلي تقريبًا. وبالمثل ، مع WebRTC ، يكون المتصفح قادرًا على الوصول إلى مقاطع الفيديو (والبيانات الأخرى) مباشرة من أجهزة الكمبيوتر.
يعد كل من WebGL و WebRTC مكتبات JavaScript يجب تضمينها في متصفح الويب. تأتي معظم متصفحات HTML5 الحديثة مزودة بدعم جزئي على الأقل لكل من واجهات برمجة التطبيقات (كما ترون هنا وهنا). ولكن كيف يمكننا تسخير هذه الأدوات في GWT المكتوبة بلغة Java؟ كما تمت مناقشته في المنشور السابق ، فإن طبقة قابلية التشغيل البيني الخاصة بـ GWT ، JsInterop (التي تم إصدارها رسميًا في GWT 2.8) تجعل هذه قطعة من الكعكة.
يعد استخدام JsInterop مع GWT 2.8 سهلاً مثل إضافة -generateJsInteropExports
كوسيطة للمترجم. يتم تحديد التعليقات التوضيحية المتاحة في الحزمة jsinterop.annotations
، مجمعة في gwt-user.jar
.
WebRTC
على سبيل المثال ، مع الحد الأدنى من أعمال الترميز ، يصبح استخدام getUserMedia
من WebRTC على Chrome مع GWT أمرًا بسيطًا مثل الكتابة:
Navigator.webkitGetUserMedia( configs, stream -> video.setSrc( URL.createObjectURL(stream) ), e -> Window.alert("Error: " + e) );
حيث يمكن تعريف فئة Navigator
على النحو التالي:
@JsType(namespace = JsPackage.GLOBAL, isNative = true, name="navigator") final static class Navigator { public static native void webkitGetUserMedia( Configs configs, SuccessCallback success, ErrorCallback error); }
من المهم تعريف الواجهات SuccessCallback
و ErrorCallback
، وكلاهما تم تنفيذه بواسطة تعبير lambda أعلاه ومعرّف في Java عن طريق التعليق التوضيحي @JsFunction
:
@JsFunction public interface SuccessCallback { public void onMediaSuccess(MediaStream stream); } @JsFunction public interface ErrorCallback { public void onError(DomException error); }
أخيرًا ، يكون تعريف URL
للفئة مطابقًا تقريبًا لتعريف Navigator
، وبالمثل ، يمكن تعريف فئة Configs
من خلال:
@JsType(namespace = JsPackage.GLOBAL, isNative = true, name="Object") public static class Configs { @JsProperty public native void setVideo(boolean getVideo); }
يتم التنفيذ الفعلي لجميع هذه الوظائف في محرك JavaScript في المتصفح.
يمكنك العثور على الكود أعلاه على جيثب هنا.
في هذا المثال ، من أجل البساطة ، يتم استخدام واجهة برمجة تطبيقات navigator.getUserMedia()
التي تم إهمالها لأنها الوحيدة التي تعمل بدون تعويض على الإصدار الثابت الحالي من Chrome. في تطبيق الإنتاج ، يمكننا استخدام adaptor.js للوصول إلى الدفق من خلال واجهة برمجة تطبيقات navigator.mediaDevices.getUserMedia()
الأحدث ، بشكل موحد في جميع المتصفحات ، ولكن هذا خارج نطاق المناقشة الحالية.
WebGL
لا يختلف استخدام WebGL من GWT كثيرًا مقارنة باستخدام WebRTC ، ولكنه أكثر تعقيدًا بعض الشيء بسبب التعقيد الجوهري لمعيار OpenGL.
نهجنا هنا يعكس النهج المتبع في القسم السابق. يمكن رؤية نتيجة الالتفاف في تطبيق GWT WebGL المستخدم في Picshare ، والذي يمكن العثور عليه هنا ، ويمكن العثور على مثال للنتائج التي تنتجها GWT هنا.
لا يمنحنا تمكين WebGL في حد ذاته إمكانية رسومات ثلاثية الأبعاد. كما كتب جريج تافاريس:
ما لا يعرفه الكثير من الناس هو أن WebGL هي في الواقع واجهة برمجة تطبيقات ثنائية الأبعاد ، وليست واجهة برمجة تطبيقات ثلاثية الأبعاد.
يجب إجراء العمليات الحسابية ثلاثية الأبعاد بواسطة كود آخر ، وتحويلها إلى صورة ثنائية الأبعاد لـ WebGL. توجد بعض مكتبات GWT الجيدة لرسومات WebGL ثلاثية الأبعاد. المفضل لدي هو Parallax ، ولكن بالنسبة للإصدار الأول من Picshare ، اتبعنا مسار "افعل ذلك بنفسك" ، وكتابة مكتبة صغيرة لتقديم شبكات ثلاثية الأبعاد بسيطة. تتيح لنا المكتبة تحديد كاميرا المنظور وإدارة مشهد الأشياء. لا تتردد في التحقق من ذلك هنا.
تجميع مكتبات Java للجهات الخارجية باستخدام GWT
NyARToolkit هو منفذ Java خالص لـ ARToolKit ، مكتبة برامج لبناء تطبيقات الواقع المعزز. تمت كتابة الميناء من قبل المطورين اليابانيين في نياتلا. على الرغم من أن ARToolKit الأصلي وإصدار Nyatla قد تباعدا إلى حد ما منذ المنفذ الأصلي ، إلا أن NyARToolkit لا يزال يتم صيانته وتحسينه بشكل نشط.
الواقع المعزز القائم على العلامات هو مجال متخصص ويتطلب الكفاءة في رؤية الكمبيوتر ومعالجة الصور الرقمية والرياضيات ، كما هو واضح هنا:
مستنسخة من وثائق ARToolKit.
مستنسخة من وثائق ARToolKit.
جميع الخوارزميات المستخدمة بواسطة مجموعة الأدوات موثقة ومفهومة جيدًا ، ولكن إعادة كتابتها من البداية عملية طويلة ومعرضة للخطأ ، لذلك يفضل استخدام مجموعة أدوات موجودة ومثبتة ، مثل ARToolKit. لسوء الحظ ، عند استهداف الويب ، لا يتوفر شيء من هذا القبيل. لا يوجد تطبيق في JavaScript ، وهي لغة تُستخدم أساسًا لمعالجة مستندات وبيانات HTML. هذا هو المكان الذي تثبت فيه GWT قوتها التي لا تقدر بثمن ، مما يتيح لنا ببساطة تحويل NyARToolkit إلى JavaScript ، واستخدامها في تطبيق ويب مع القليل من المتاعب.
تجميع مع GWT
نظرًا لأن مشروع GWT هو في الأساس مشروع Java ، فإن استخدام NyARToolkit هو مجرد مسألة استيراد الملفات المصدر في مسار المصدر الخاص بك. ومع ذلك ، لاحظ أنه نظرًا لأن تحويل شفرة GWT إلى JavaScript يتم على مستوى كود المصدر ، فأنت بحاجة إلى مصادر NyARToolkit ، وليس مجرد JAR مع الفئات المترجمة.
يمكن العثور على المكتبة التي تستخدمها Picshare هنا. يعتمد فقط على الحزم الموجودة داخل نظام lib/src
و lib/src.markersystem
من بناء NyARToolkit المؤرشف هنا. يجب علينا نسخ واستيراد هذه الحزم إلى مشروع GWT الخاص بنا.

يجب أن نبقي حزم الطرف الثالث منفصلة عن التنفيذ الخاص بنا ، ولكن للمضي قدمًا في "GWT-ization" لـ NyARToolkit ، يجب علينا توفير ملف تكوين XML لإعلام برنامج التحويل البرمجي GWT بمكان البحث عن المصادر. في الحزمة jp.nyatla.nyartoolkit
نضيف الملف NyARToolkit.gwt.xml
<module> <source path="core" /> <source path="detector" /> <source path="nyidmarker" /> <source path="processor" /> <source path="psarplaycard" /> <source path="markersystem" /> </module>
الآن ، في الحزمة الرئيسية الخاصة بنا ، com.jooink.gwt.nyartoolkit
، نقوم بإنشاء ملف التكوين الرئيسي ، GWT_NyARToolKit.gwt.xml
، ونطلب من المترجم تضمين مصدر Nyatla في classpath عن طريق التوريث من ملف XML الخاص به:
<inherits name='jp.nyatla.nyartoolkit.NyARToolkit'/>
سهل جدا ، في الواقع. في معظم الحالات ، سيكون هذا هو كل ما يتطلبه الأمر ، لكن للأسف لم ننتهي بعد. إذا حاولنا التجميع أو التنفيذ من خلال وضع Super Dev Mode في هذه المرحلة ، فإننا نواجه خطأ يفيد ، بشكل مفاجئ:
No source code is available for type java.io.InputStream; did you forget to inherit a required module?
والسبب في ذلك هو أن NyARToolkit (أي مكتبة Java مخصصة لمشاريع Java) تستخدم فئات من JRE غير المدعومة من قبل JRE's Emulated GWT. ناقشنا هذا باختصار في الوظيفة السابقة.
في هذه الحالة ، تكمن المشكلة في InputStream
وفئات الإدخال والإخراج ذات الصلة. كما يحدث ، لا نحتاج حتى إلى استخدام معظم هذه الفئات ، لكننا بحاجة إلى توفير بعض التنفيذ للمترجم. حسنًا ، يمكننا استثمار الكثير من الوقت في إزالة هذه المراجع يدويًا من مصدر NyARToolkit ، لكن هذا سيكون جنونًا. تعطينا GWT حلاً أفضل: توفير عمليات التنفيذ الخاصة بنا للفئات غير المدعومة من خلال علامة XML <super-source>
.
<super-source>
كما هو موضح في الوثائق الرسمية:
تقوم العلامة
<super-source>
بتوجيه المترجم لإعادة إنشاء الجذر لمسار المصدر. هذا مفيد للحالات التي تريد فيها إعادة استخدام Java API موجود لمشروع GWT ، لكن المصدر الأصلي غير متاح أو غير قابل للترجمة. والسبب الشائع لهذا هو محاكاة جزء من JRE لم يتم تنفيذه بواسطة GWT.
لذا فإن <super-source>
هو بالضبط ما نحتاجه.
يمكننا إنشاء دليل jre
في مشروع GWT ، حيث يمكننا وضع تطبيقاتنا للفئات التي تسبب لنا مشاكل:
java.io.FileInputStream java.io.InputStream java.io.InputStreamReader java.io.StreamTokenizer java.lang.reflect.Array java.nio.ByteBuffer java.nio.ByteOrder
كل هذه الأشياء ، باستثناء java.lang.reflect.Array
، غير مستخدمة حقًا ، لذلك نحتاج فقط إلى تطبيقات غبية. على سبيل المثال ، يقرأ FileInputStream
بنا على النحو التالي:
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; } }
يعتبر بيان Window.alert
في المنشئ مفيدًا أثناء التطوير. على الرغم من أننا يجب أن نكون قادرين على تجميع الفصل ، إلا أننا نريد التأكد من أننا لا نستخدمه أبدًا في الواقع ، لذلك سينبهنا هذا في حالة استخدام الفصل عن غير قصد.
يتم استخدام java.lang.reflect.Array
بالفعل بواسطة الكود الذي نحتاجه ، لذلك يلزم تنفيذ غير غبي تمامًا. هذا هو رمزنا:
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; } }
الآن إذا وضعنا <super-source path="jre"/>
في ملف الوحدة GWT_NyARToolkit.gwt.xml
، فيمكننا تجميع واستخدام مجموعة NyARToolkit بأمان في مشروعنا!
لصق كل شيء مع GWT
نحن الآن في وضع نمتلك فيه:
- WebRTC ، تقنية قادرة على نقل البث من كاميرا الويب وعرضها في علامة
<video>
. - WebGL ، تقنية قادرة على معالجة الرسومات المسرَّعة بالأجهزة في HTML
<canvas>
. - NyARToolkit ، مكتبة Java قادرة على التقاط صورة (كمجموعة من وحدات البكسل) ، والبحث عن علامة ، وإذا وجدت ، فإنها تمنحنا مصفوفة تحويل تحدد تمامًا موضع العلامة في الفضاء ثلاثي الأبعاد.
التحدي الآن هو دمج كل هذه التقنيات معًا.
لن نتعمق في كيفية تحقيق ذلك ، ولكن الفكرة الأساسية هي استخدام صور الفيديو كخلفية لمشهدنا (نسيج مطبق على المستوى "البعيد" في الصورة أعلاه) وبناء هيكل بيانات ثلاثي الأبعاد مما يسمح لنا بإسقاط هذه الصورة في الفضاء باستخدام نتائج NyARToolkit.
يمنحنا هذا البناء الهيكل الصحيح للتفاعل مع مكتبة NyARToolkit للتعرف على العلامات ، ورسم نموذج ثلاثي الأبعاد أعلى مشهد الكاميرا.
إن جعل دفق الكاميرا قابلاً للاستخدام أمر صعب بعض الشيء. يمكن رسم بيانات الفيديو فقط إلى عنصر <video>
. عنصر HTML5 <video>
معتم ، ولا يسمح لنا باستخراج بيانات الصورة مباشرة ، لذلك فنحن مضطرون لنسخ الفيديو إلى <canvas>
وسيط ، واستخراج بيانات الصورة ، وتحويلها إلى مصفوفة من البكسل ، وأخيرًا ادفعه إلى طريقة Sensor.update()
الخاصة بـ NyARToolkit. ثم يمكن لـ NyARToolkit القيام بعمل تحديد العلامة في الصورة ، وإرجاع مصفوفة التحويل المقابلة لموقعها في الفضاء ثلاثي الأبعاد الخاص بنا.
باستخدام هذه العناصر ، يمكننا تجميع كائن اصطناعي بالضبط فوق العلامة ، بثلاثية الأبعاد ، في دفق الفيديو المباشر! بفضل الأداء العالي لـ GWT ، لدينا الكثير من الموارد الحسابية ، لذلك يمكننا حتى تطبيق بعض تأثيرات الفيديو على اللوحة القماشية ، مثل البني الداكن أو التمويه ، قبل استخدامها كخلفية لمشهد WebGL.
يصف الكود المختصر التالي جوهر العملية:
// 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 ... } ...
باستخدام هذه التقنية ، يمكننا الحصول على نتائج مثل هذا:
هذه هي العملية التي استخدمناها لإنشاء Picshare ، حيث تتم دعوتك لطباعة علامة أو عرضها على هاتفك المحمول ، واللعب باستخدام الواقع المعزز القائم على العلامة في متصفحك. استمتع!
الملاحظات الختامية
Picshare هو مشروع طويل الأمد للحيوانات الأليفة بالنسبة لنا في Jooink. يعود التنفيذ الأول إلى بضع سنوات ، ومع ذلك كان سريعًا بما يكفي ليكون مثيرًا للإعجاب. في هذا الرابط ، يمكنك رؤية إحدى تجاربنا السابقة ، التي تم تجميعها في عام 2012 ولم يتم لمسها أبدًا. لاحظ أنه يوجد في العينة <video>
واحد فقط. النافذتان الأخريان عبارة عن عناصر <canvas>
تعرضان نتائج المعالجة.
كان GWT قويًا بدرجة كافية حتى في عام 2012. مع إصدار GWT 2.8 ، اكتسبنا طبقة إمكانية التشغيل البيني المحسنة كثيرًا باستخدام JsInterop ، مما أدى إلى زيادة تعزيز الأداء. أيضًا ، للاحتفال بالكثيرين ، اكتسبنا أيضًا بيئة تطوير وتصحيح أفضل بكثير ، وضع Super Dev. أوه نعم ، ودعم جافا 8.
نحن نتطلع إلى GWT 3.0!