1. قبل البدء
يعلّمك هذا الدرس التطبيقي كيفية استخدام الميزات المستندة إلى WebGL في Maps JavaScript API للتحكّم في الخريطة المتجهة وعرضها بثلاثة أبعاد.
المتطلبات الأساسية
يفترض هذا الدرس التطبيقي أن لديك معرفة متوسطة بلغة JavaScript وواجهة Maps JavaScript API. للتعرّف على أساسيات استخدام Maps JS API، جرِّب الدرس التطبيقي حول الترميز: إضافة خريطة إلى موقعك الإلكتروني (JavaScript).
أهداف الدورة التعليمية
- إنشاء معرّف خريطة مع تفعيل الخريطة الاتجاهية في JavaScript
- التحكّم في الخريطة من خلال الإمالة والتدوير آليًا
- عرض عناصر ثلاثية الأبعاد على الخريطة باستخدام
WebGLOverlayView
وThree.js - إضافة حركة إلى الكاميرا باستخدام
moveCamera
المتطلبات
- حساب على Google Cloud Platform تم تفعيل الفوترة فيه
- مفتاح واجهة برمجة تطبيقات Google Maps Platform مع تفعيل Maps JavaScript API
- معرفة متوسطة بلغات JavaScript وHTML وCSS
- محرّر نصوص أو بيئة تطوير متكاملة (IDE) من اختيارك
- Node.js
2. طريقة الإعداد
في خطوة التفعيل أدناه، عليك تفعيل Maps JavaScript API.
إعداد Google Maps Platform
إذا لم يكن لديك حساب على Google Cloud Platform ومشروع مفعَّل فيه نظام الفوترة، يُرجى الاطّلاع على دليل البدء باستخدام Google Maps Platform لإنشاء حساب فوترة ومشروع.
- في Cloud Console، انقر على القائمة المنسدلة الخاصة بالمشروع واختَر المشروع الذي تريد استخدامه في هذا الدرس العملي.
- فعِّل واجهات برمجة التطبيقات وحِزم تطوير البرامج (SDK) في Google Maps Platform المطلوبة لهذا الدرس العملي في Google Cloud Marketplace. لإجراء ذلك، اتّبِع الخطوات الواردة في هذا الفيديو أو هذه المستندات.
- أنشئ مفتاح واجهة برمجة التطبيقات في صفحة بيانات الاعتماد في Cloud Console. يمكنك اتّباع الخطوات الواردة في هذا الفيديو أو هذه المستندات. تتطلّب جميع الطلبات إلى "منصة خرائط Google" مفتاح واجهة برمجة تطبيقات.
إعداد Node.js
إذا لم يكن لديك Node.js، انتقِل إلى https://nodejs.org/ لتنزيل وقت تشغيل Node.js وتثبيته على جهاز الكمبيوتر.
يتضمّن Node.js مدير حزم npm الذي تحتاج إليه لتثبيت التبعيات لهذا الدرس التطبيقي حول الترميز.
تنزيل نموذج بداية المشروع
قبل البدء في هذا الدرس العملي، اتّبِع الخطوات التالية لتنزيل نموذج المشروع المبدئي، بالإضافة إلى رمز الحلّ الكامل:
- نزِّل مستودع GitHub الخاص بهذا الدرس العملي أو أنشئ نسخة منه على https://github.com/googlecodelabs/maps-platform-101-webgl/. يقع مشروع البداية في الدليل
/starter
ويتضمّن بنية الملف الأساسية التي تحتاج إليها لإكمال الدرس التطبيقي. يمكنك العثور على كل ما تحتاج إليه للعمل في الدليل/starter/src
. - بعد تنزيل مشروع البداية، شغِّل الأمر
npm install
في الدليل/starter
. يؤدي ذلك إلى تثبيت جميع العناصر التابعة اللازمة والمدرَجة فيpackage.json
. - بعد تثبيت التبعيات، شغِّل
npm start
في الدليل.
تم إعداد مشروع البداية لتتمكّن من استخدام webpack-dev-server، الذي يجمع الرمز الذي تكتبه ويشغّله محليًا. يعيد webpack-dev-server أيضًا تحميل تطبيقك تلقائيًا في المتصفّح كلما أجريت تغييرات على الرمز.
إذا أردت الاطّلاع على رمز الحلّ الكامل قيد التشغيل، يمكنك إكمال خطوات الإعداد أعلاه في الدليل /solution
.
إضافة مفتاح واجهة برمجة التطبيقات
يتضمّن التطبيق المبدئي كل الرموز اللازمة لتحميل الخريطة باستخدام JS API Loader، لذا كل ما عليك فعله هو تقديم مفتاح واجهة برمجة التطبيقات ومعرّف الخريطة. أداة تحميل JS API هي مكتبة بسيطة تجرّد الطريقة التقليدية لتحميل Maps JavaScript API المضمّنة في نموذج HTML باستخدام علامة script
، ما يتيح لك التعامل مع كل شيء في رمز JavaScript.
لإضافة مفتاح واجهة برمجة التطبيقات، اتّبِع الخطوات التالية في المشروع المبدئي:
- فتح "
app.js
" - في الكائن
apiOptions
، اضبط مفتاح واجهة برمجة التطبيقات كقيمةapiOptions.apiKey
.
3- إنشاء معرّف خريطة واستخدامه
لاستخدام الميزات المستندة إلى WebGL في Maps JavaScript API، يجب توفير رقم تعريف خريطة مع تفعيل الخريطة المتجهة.
إنشاء معرّف خريطة
- في Google Cloud Console، انتقِل إلى "منصة خرائط Google" > "إدارة الخرائط".
- انقر على "إنشاء معرّف خريطة جديد".
- في حقل "اسم الخريطة"، أدخِل اسمًا لمعرّف الخريطة.
- في القائمة المنسدلة "نوع الخريطة" (Map type)، اختَر "JavaScript". سيظهر خيار "خيارات JavaScript".
- ضمن "خيارات JavaScript"، انقر على زر الاختيار "المتجه"، ومربّع الاختيار "الإمالة"، ومربّع الاختيار "التدوير".
- Optional. في حقل "الوصف"، أدخِل وصفًا لمفتاح واجهة برمجة التطبيقات.
- انقر على الزرّ "التالي". ستظهر صفحة "تفاصيل معرّف الخريطة".
- انسخ رقم تعريف الخريطة. ستستخدم هذا الرمز في الخطوة التالية لتحميل الخريطة.
استخدام معرّف خريطة
لتحميل خريطة المتجهات، يجب تقديم معرّف خريطة كسمة في الخيارات عند إنشاء مثيل للخريطة. يمكنك أيضًا تقديم معرّف الخريطة نفسه عند تحميل Maps JavaScript API.
لتحميل الخريطة باستخدام رقم تعريف الخريطة، اتّبِع الخطوات التالية:
- اضبط معرّف الخريطة كقيمة
mapOptions.mapId
.
عند إنشاء الخريطة، يتيح لك توفير معرّف الخريطة إخبار "منصة خرائط Google" بالخريطة التي تريد تحميلها في مثيل معيّن. يمكنك إعادة استخدام معرّف الخريطة نفسه في تطبيقات متعددة أو طرق عرض متعددة ضمن التطبيق نفسه.const mapOptions = { "tilt": 0, "heading": 0, "zoom": 18, "center": { lat: 35.6594945, lng: 139.6999859 }, "mapId": "YOUR_MAP_ID" };
تحقَّق من التطبيق الذي يعمل في المتصفّح. يجب تحميل الخريطة المتجهة مع تفعيل ميزة الإمالة والتدوير بنجاح. للتأكّد من تفعيل الإمالة والتدوير، اضغط مع الاستمرار على مفتاح Shift واسحب باستخدام الماوس أو استخدِم مفاتيح الأسهم على لوحة المفاتيح.
إذا لم يتم تحميل الخريطة، تأكَّد من أنّك قدّمت مفتاح API صالحًا في apiOptions
. إذا لم يكن بإمكان الخريطة الإمالة والتدوير، تأكَّد من أنّك قدّمت معرّف خريطة مع تفعيل الإمالة والتدوير في apiOptions
وmapOptions
.
يجب أن يظهر ملف app.js
الآن على النحو التالي:
import { Loader } from '@googlemaps/js-api-loader';
const apiOptions = {
"apiKey": 'YOUR_API_KEY',
};
const mapOptions = {
"tilt": 0,
"heading": 0,
"zoom": 18,
"center": { lat: 35.6594945, lng: 139.6999859 },
"mapId": "YOUR_MAP_ID"
}
async function initMap() {
const mapDiv = document.getElementById("map");
const apiLoader = new Loader(apiOptions);
await apiLoader.load();
return new google.maps.Map(mapDiv, mapOptions);
}
function initWebGLOverlayView (map) {
let scene, renderer, camera, loader;
// WebGLOverlayView code goes here
}
(async () => {
const map = await initMap();
})();
4. تنفيذ WebGLOverlayView
تمنحك السمة WebGLOverlayView
إمكانية الوصول المباشر إلى سياق عرض WebGL نفسه المستخدَم لعرض الخريطة الأساسية المتجهة. وهذا يعني أنّه يمكنك عرض عناصر ثنائية وثلاثية الأبعاد مباشرةً على الخريطة باستخدام WebGL، بالإضافة إلى مكتبات الرسومات الشائعة المستندة إلى WebGL.
تعرض WebGLOverlayView
خمس نقاط ربط في دورة حياة سياق عرض WebGL للخريطة يمكنك استخدامها. في ما يلي وصف سريع لكل خطاف والحالات التي يجب استخدامه فيها:
onAdd()
: يتم استدعاؤها عند إضافة التراكب إلى خريطة من خلال استدعاءsetMap
على مثيلWebGLOverlayView
. هذا هو المكان الذي يجب أن تنفّذ فيه أي عمل مرتبط بـ WebGL ولا يتطلّب الوصول المباشر إلى سياق WebGL.-
onContextRestored()
: يتم استدعاؤه عندما يصبح سياق WebGL متاحًا ولكن قبل عرض أي شيء. هذا هو المكان الذي يجب فيه تهيئة العناصر وربط الحالة وتنفيذ أي إجراء آخر يتطلّب الوصول إلى سياق WebGL ولكن يمكن تنفيذه خارج استدعاءonDraw()
. يتيح لك ذلك إعداد كل ما تحتاج إليه بدون إضافة تكاليف إضافية إلى العرض الفعلي للخريطة، والذي يتطلّب الكثير من وحدة معالجة الرسومات. -
onDraw()
: يتم استدعاؤها مرة واحدة لكل إطار عندما يبدأ WebGL في عرض الخريطة وأي عناصر أخرى طلبتها. يجب أن تقلّل قدر الإمكان من العمليات التي تجريها فيonDraw()
لتجنُّب حدوث مشاكل في أداء عرض الخريطة. -
onContextLost()
: يتم استدعاؤه عند فقدان سياق عرض WebGL لأي سبب. -
onRemove()
: يتم استدعاؤها عند إزالة التراكب من الخريطة من خلال استدعاءsetMap(null)
على مثيلWebGLOverlayView
.
في هذه الخطوة، ستنشئ مثيلاً من WebGLOverlayView
وتنفّذ ثلاثًا من دوال ربط مراحل النشاط الخاصة به: onAdd
وonContextRestored
وonDraw
. للحفاظ على تنظيم التعليمات البرمجية وتسهيل تتبُّعها، سيتم التعامل مع جميع التعليمات البرمجية الخاصة بالتراكب في الدالة initWebGLOverlayView()
المتوفّرة في نموذج البداية لهذا الدرس العملي.
- أنشئ مثيلاً من
WebGLOverlayView()
.
يتم توفير التراكب من خلال Maps JavaScript API فيgoogle.maps.WebGLOverlayView
. للبدء، أنشئ مثيلاً بإضافة ما يلي إلىinitWebGLOverlayView()
:const webGLOverlayView = new google.maps.WebGLOverlayView();
- تنفيذ عمليات ربط مراحل النشاط
لتنفيذ خطافات مراحل النشاط، أضِف ما يلي إلىinitWebGLOverlayView()
:webGLOverlayView.onAdd = () => {}; webGLOverlayView.onContextRestored = ({gl}) => {}; webGLOverlayView.onDraw = ({gl, transformer}) => {};
- أضِف مثيل التراكب إلى الخريطة.
الآن، اتّصِل بـsetMap()
في مثيل التراكب وأدخِل الخريطة عن طريق إضافة ما يلي إلىinitWebGLOverlayView()
:webGLOverlayView.setMap(map)
- تواصل هاتفيًا مع "
initWebGLOverlayView
".
الخطوة الأخيرة هي تنفيذinitWebGLOverlayView()
من خلال إضافة ما يلي إلى الدالة التي يتم استدعاؤها على الفور في أسفلapp.js
:initWebGLOverlayView(map);
يجب أن تبدو initWebGLOverlayView
والدالة التي يتم استدعاؤها على الفور الآن على النحو التالي:
async function initWebGLOverlayView (map) {
let scene, renderer, camera, loader;
const webGLOverlayView = new google.maps.WebGLOverlayView();
webGLOverlayView.onAdd = () => {}
webGLOverlayView.onContextRestored = ({gl}) => {}
webGLOverlayView.onDraw = ({gl, transformer}) => {}
webGLOverlayView.setMap(map);
}
(async () => {
const map = await initMap();
initWebGLOverlayView(map);
})();
هذا كل ما تحتاج إليه لتنفيذ WebGLOverlayView
. بعد ذلك، ستُعدّ كل ما تحتاج إليه لعرض عنصر ثلاثي الأبعاد على الخريطة باستخدام Three.js.
5- إعداد مشهد three.js
قد يكون استخدام WebGL معقّدًا للغاية لأنّه يتطلّب منك تحديد جميع جوانب كل عنصر يدويًا ثم بعض الجوانب الأخرى. لتسهيل الأمور، ستستخدم في هذا الدرس التطبيقي Three.js، وهي مكتبة رسومات شائعة توفّر طبقة تجريدية مبسطة فوق WebGL. تتضمّن مكتبة Three.js مجموعة كبيرة ومتنوعة من دوال التيسير التي تنفّذ كل العمليات، بدءًا من إنشاء أداة عرض WebGL إلى رسم أشكال العناصر الشائعة الثنائية والثلاثية الأبعاد، وصولاً إلى التحكّم في الكاميرات وعمليات تحويل العناصر وغير ذلك الكثير.
هناك ثلاثة أنواع أساسية من العناصر في Three.js مطلوبة لعرض أي شيء:
- المشهد: "حاوية" يتم فيها عرض جميع الكائنات ومصادر الإضاءة والتركيبات وما إلى ذلك.
- الكاميرا: هي كاميرا تمثّل نقطة عرض المشهد. تتوفّر أنواع متعددة من الكاميرات، ويمكن إضافة كاميرا واحدة أو أكثر إلى مشهد واحد.
- أداة العرض: أداة عرض تتولّى معالجة جميع العناصر في المشهد وعرضها. في Three.js، يتم استخدام
WebGLRenderer
بشكل شائع، ولكن تتوفّر بعض الخيارات الأخرى كبدائل في حال لم يكن WebGL متاحًا على جهاز العميل.
في هذه الخطوة، ستحمّل جميع العناصر التابعة اللازمة لـ Three.js وتعدّ مشهدًا أساسيًا.
- تحميل three.js
ستحتاج إلى عنصرَين تابعَين في هذا الدرس العملي: مكتبة Three.js وGLTF Loader، وهي فئة تتيح لك تحميل عناصر ثلاثية الأبعاد بتنسيق GL Trasmission Format (gLTF). توفّر مكتبة Three.js أدوات تحميل متخصّصة للعديد من تنسيقات الكائنات الثلاثية الأبعاد المختلفة، ولكن يُنصح باستخدام gLTF.
في الرمز البرمجي أدناه، يتم استيراد مكتبة Three.js بأكملها. في تطبيق مخصّص للإنتاج، من المحتمل أنّك تريد استيراد الفئات التي تحتاج إليها فقط، ولكن في هذا الدرس البرمجي، استورِد المكتبة بأكملها لتبسيط الأمور. يُرجى العِلم أيضًا أنّ أداة تحميل GLTF غير مضمّنة في المكتبة التلقائية، ويجب استيرادها من مسار منفصل في التبعية، وهو المسار الذي يمكنك من خلاله الوصول إلى جميع أدوات التحميل التي توفّرها Three.js.
لاستيراد Three.js وGLTF Loader، أضِف ما يلي إلى أعلىapp.js
:import * as THREE from 'three'; import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js';
- إنشاء مشهد ثلاثي الأبعاد باستخدام three.js
لإنشاء مشهد، أنشئ مثيلاً لفئةScene
Three.js من خلال إضافة ما يلي إلى خطافonAdd
:scene = new THREE.Scene();
- أضِف كاميرا إلى المشهد.
كما ذكرنا سابقًا، تمثّل الكاميرا منظور العرض للمشهد، وتحدّد طريقة تعامل Three.js مع العرض المرئي للعناصر داخل المشهد. بدون كاميرا، لا يمكن "رؤية" المشهد، ما يعني أنّ العناصر لن تظهر لأنّه لن يتم عرضها.
توفّر مكتبة Three.js مجموعة متنوعة من الكاميرات المختلفة التي تؤثر في طريقة تعامل أداة العرض مع العناصر من حيث المنظور والعمق وغير ذلك. في هذا المشهد، ستستخدمPerspectiveCamera
، وهو نوع الكاميرا الأكثر استخدامًا في Three.js، والمصمَّم لمحاكاة الطريقة التي ترى بها العين البشرية المشهد. وهذا يعني أنّ العناصر الأبعد عن الكاميرا ستظهر أصغر من العناصر الأقرب، وسيكون للمشهد نقطة تلاشٍ، وغير ذلك.
لإضافة كاميرا منظور إلى المشهد، أضِف ما يلي إلى خطافonAdd
: باستخدامcamera = new THREE.PerspectiveCamera();
PerspectiveCamera
، يمكنك أيضًا ضبط السمات التي تشكّل نقطة العرض، بما في ذلك المستويان القريب والبعيد ونسبة العرض إلى الارتفاع ومجال الرؤية (fov). تشكّل هذه السمات معًا ما يُعرف باسم المخروط الناقص، وهو مفهوم مهم يجب فهمه عند العمل في بيئة ثلاثية الأبعاد، ولكنّه خارج نطاق هذا الدرس العملي. سيكون إعدادPerspectiveCamera
التلقائي كافيًا. - أضِف مصادر إضاءة إلى المشهد.
بشكلٍ تلقائي، ستظهر الكائنات المعروضة في مشهد Three.js باللون الأسود، بغض النظر عن الأنسجة المطبَّقة عليها. ويرجع ذلك إلى أنّ مشهد Three.js يحاكي طريقة تفاعل العناصر في العالم الحقيقي، حيث تعتمد إمكانية رؤية اللون على الضوء المنعكس من أحد العناصر. باختصار، لا ضوء، لا لون.
يوفّر Three.js مجموعة متنوعة من أنواع الإضاءة المختلفة التي ستستخدم منها نوعَين: AmbientLight
: يوفّر مصدر ضوء منتشرًا يضيء جميع العناصر في المشهد بالتساوي من جميع الزوايا. سيمنح ذلك المشهد كمية أساسية من الضوء لضمان ظهور الأنسجة على جميع العناصر.DirectionalLight
: يوفّر ضوءًا قادمًا من اتجاه معيّن في المشهد. على عكس طريقة عمل الضوء الموضعي في العالم الحقيقي، تكون أشعة الضوء المنبعثة منDirectionalLight
متوازية ولا تنتشر أو تتشتت كلما ابتعدت عن مصدر الضوء.
يمكنك ضبط لون وشدة كل ضوء لإنشاء تأثيرات إضاءة مجمّعة. على سبيل المثال، في الرمز البرمجي أدناه، توفّر الإضاءة المحيطة ضوءًا أبيض ناعمًا للمشهد بأكمله، بينما توفّر الإضاءة الاتجاهية ضوءًا ثانويًا يضيء العناصر بزاوية مائلة للأسفل. في حالة الضوء الاتجاهي، يتم ضبط الزاوية باستخدامposition.set(x, y ,z)
، حيث تكون كل قيمة مرتبطة بالمحور المعني. على سبيل المثال، سيؤدي ضبط القيمة علىposition.set(0,1,0)
إلى وضع مصدر الضوء مباشرةً فوق المشهد على المحور y مع توجيهه للأسفل مباشرةً.
لإضافة مصادر الإضاءة إلى المشهد، أضِف ما يلي إلى خطافonAdd
:const ambientLight = new THREE.AmbientLight( 0xffffff, 0.75 ); scene.add(ambientLight); const directionalLight = new THREE.DirectionalLight(0xffffff, 0.25); directionalLight.position.set(0.5, -1, 0.5); scene.add(directionalLight);
يجب أن يبدو خطاف onAdd
الآن على النحو التالي:
webGLOverlayView.onAdd = () => {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera();
const ambientLight = new THREE.AmbientLight( 0xffffff, 0.75 );
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.25);
directionalLight.position.set(0.5, -1, 0.5);
scene.add(directionalLight);
}
تم الآن إعداد المشهد وأصبح جاهزًا للعرض. بعد ذلك، ستضبط أداة العرض WebGL وتعرض المشهد.
6. عرض المشهد
حان الوقت لعرض المشهد. حتى هذه المرحلة، يتم إعداد كل ما أنشأته باستخدام Three.js في الرمز، ولكنّه غير موجود بشكل أساسي لأنّه لم يتم عرضه بعد في سياق عرض WebGL. تعرض WebGL المحتوى الثنائي والثلاثي الأبعاد في المتصفح باستخدام واجهة برمجة تطبيقات لوحة الرسم. إذا سبق لك استخدام Canvas API، من المحتمل أنّك على دراية context
في لوحة HTML، وهو المكان الذي يتم فيه عرض كل شيء. قد لا تعرف أنّ هذه الواجهة تعرض سياق عرض رسومات OpenGL من خلال واجهة برمجة التطبيقات WebGLRenderingContext
في المتصفّح.
لتسهيل التعامل مع أداة العرض WebGL، توفّر Three.js WebGLRenderer
، وهي أداة تضمين تسهّل نسبيًا ضبط سياق العرض WebGL كي تتمكّن Three.js من عرض المشاهد في المتصفّح. في حالة الخريطة، لا يكفي عرض مشهد Three.js في المتصفّح بجانب الخريطة. يجب أن يتم عرض Three.js في سياق العرض نفسه تمامًا كما هو الحال مع الخريطة، وذلك حتى يتم عرض كل من الخريطة وأي عناصر من مشهد Three.js في مساحة العالم نفسها. يتيح ذلك للمُعرِض التعامل مع التفاعلات بين العناصر على الخريطة والعناصر في المشهد، مثل الحجب، وهو طريقة رائعة للتعبير عن أنّ أحد العناصر سيخفي العناصر التي تقع خلفه عن الأنظار.
يبدو الأمر معقّدًا جدًا، أليس كذلك؟ لحسن الحظ، يمكن الاستعانة بمكتبة Three.js مرة أخرى.
- إعداد أداة العرض WebGL
عند إنشاء مثيل جديد من Three.jsWebGLRenderer
، يمكنك تزويده بسياق العرض الخاص بـ WebGL الذي تريد أن يعرض المشهد فيه. هل تتذكر وسيطةgl
التي يتم تمريرها إلى خطافonContextRestored
؟ يمثّل الكائنgl
سياق عرض WebGL للخريطة. كل ما عليك فعله هو تقديم السياق ولوحة العرض وسماتها إلى مثيلWebGLRenderer
، وكلّها متاحة من خلال العنصرgl
. في هذا الرمز، تم أيضًا ضبط السمةautoClear
الخاصة بأداة العرض علىfalse
حتى لا تمحو أداة العرض ناتجها في كل إطار.
لضبط أداة العرض، أضِف ما يلي إلى خطافonContextRestored
:renderer = new THREE.WebGLRenderer({ canvas: gl.canvas, context: gl, ...gl.getContextAttributes(), }); renderer.autoClear = false;
- عرض المشهد
بعد ضبط أداة العرض، استدعِrequestRedraw
على مثيلWebGLOverlayView
لإخبار التراكب بأنّه يجب إعادة الرسم عند عرض الإطار التالي، ثم استدعِrender
على أداة العرض ومرِّر إليها مشهد Three.js والكاميرا لعرضهما. أخيرًا، امحِ حالة سياق عرض WebGL. هذه خطوة مهمة لتجنُّب تعارضات حالة GL، لأنّ استخدام WebGL Overlay View يعتمد على حالة GL مشترَكة. إذا لم تتم إعادة ضبط الحالة في نهاية كل طلب رسم، قد تتسبّب تعارضات حالة GL في تعذُّر عمل أداة العرض.
لإجراء ذلك، أضِف ما يلي إلى خطافonDraw
ليتم تنفيذه في كل إطار:webGLOverlayView.requestRedraw(); renderer.render(scene, camera); renderer.resetState();
يجب أن تبدو الآن خطافات onContextRestored
وonDraw
على النحو التالي:
webGLOverlayView.onContextRestored = ({gl}) => {
renderer = new THREE.WebGLRenderer({
canvas: gl.canvas,
context: gl,
...gl.getContextAttributes(),
});
renderer.autoClear = false;
}
webGLOverlayView.onDraw = ({gl, transformer}) => {
webGLOverlayView.requestRedraw();
renderer.render(scene, camera);
renderer.resetState();
}
7. عرض تصميم ثلاثي الأبعاد على الخريطة
حسنًا، لقد أصبحت كل المعلومات متوفرة لديك. لقد أعددت WebGL Overlay View وأنشأت مشهد Three.js، ولكن هناك مشكلة واحدة: لا يوجد أي شيء فيه. بعد ذلك، حان الوقت لعرض عنصر ثلاثي الأبعاد في المشهد. لإجراء ذلك، ستستخدم أداة تحميل GLTF التي استوردتها سابقًا.
تتوفّر التصاميم الثلاثية الأبعاد بتنسيقات مختلفة، ولكن بالنسبة إلى Three.js، يُفضّل استخدام تنسيق gLTF بسبب حجمه وأدائه أثناء التشغيل. في هذا الدرس العملي، يتم توفير نموذج لعرضه في المشهد في /src/pin.gltf
.
- أنشئ مثيلاً لبرنامج تحميل النماذج.
أضِف ما يلي إلىonAdd
:loader = new GLTFLoader();
- حمِّل تصميمًا ثلاثي الأبعاد.
أدوات تحميل النماذج غير متزامنة وتنفّذ دالة ردّ اتصال بعد تحميل النموذج بالكامل. لتحميلpin.gltf
، أضِف ما يلي إلىonAdd
:const source = "pin.gltf"; loader.load( source, gltf => {} );
- أضِف النموذج إلى المشهد.
يمكنك الآن إضافة النموذج إلى المشهد عن طريق إلحاق ما يلي بدالة الاستدعاءloader
. يُرجى العِلم أنّه تتم إضافةgltf.scene
وليسgltf
:scene.add(gltf.scene);
- إعداد مصفوفة عرض الكاميرا
آخر خطوة عليك اتّخاذها لضمان عرض النموذج بشكل صحيح على الخريطة هي ضبط مصفوفة العرض للكاميرا في مشهد Three.js. يتم تحديد مصفوفة العرض على شكل مصفوفةMatrix4
في Three.js، وهي تحدد نقطة في مساحة ثلاثية الأبعاد مع عمليات التحويل، مثل عمليات التدوير والقص والقياس وغيرها.
في حالةWebGLOverlayView
، يتم استخدام مصفوفة العرض لتحديد مكان وكيفية عرض مشهد Three.js بالنسبة إلى الخريطة الأساسية. لكن هناك مشكلة. يتم تحديد المواقع الجغرافية على الخريطة كأزواج من إحداثيات خط العرض وخط الطول، بينما يتم تحديد المواقع الجغرافية في مشهد Three.js كإحداثياتVector3
. وكما قد تتوقّع، فإنّ احتساب معدّل الإحالات الناجحة بين النظامَين ليس أمرًا بسيطًا. لحلّ هذه المشكلة، يمرِّرWebGLOverlayView
كائنcoordinateTransformer
إلى خطاف دورة الحياةOnDraw
الذي يحتوي على دالة تُسمىfromLatLngAltitude
. تأخذ الدالةfromLatLngAltitude
العنصرLatLngAltitude
أوLatLngAltitudeLiteral
، ومجموعة من الوسيطات التي تحدّد عملية تحويل المشهد بشكل اختياري، ثم تحوّلها إلى مصفوفة عرض نموذجية. كل ما عليك فعله هو تحديد المكان الذي تريد عرض مشهد Three.js فيه على الخريطة، بالإضافة إلى طريقة تحويله، وسيتولّىWebGLOverlayView
تنفيذ بقية الخطوات. يمكنك بعد ذلك تحويل مصفوفة MVP إلى مصفوفةMatrix4
Three.js وضبط مصفوفة عرض الكاميرا عليها.
في الرمز البرمجي أدناه، يطلب الوسيط الثاني من WebGL Overlay View ضبط ارتفاع مشهد Three.js على 120 مترًا فوق سطح الأرض، ما سيجعل النموذج يبدو وكأنّه يطفو.
لضبط مصفوفة عرض الكاميرا، أضِف ما يلي إلى الخطافonDraw
:const latLngAltitudeLiteral = { lat: mapOptions.center.lat, lng: mapOptions.center.lng, altitude: 120 } const matrix = transformer.fromLatLngAltitude(latLngAltitudeLiteral); camera.projectionMatrix = new THREE.Matrix4().fromArray(matrix);
- حوِّل النموذج.
ستلاحظ أنّ الدبوس لا يقع بشكل عمودي على الخريطة. في الرسومات الثلاثية الأبعاد، بالإضافة إلى مساحة العالم التي تحتوي على محاور س وص وعين الخاصة بها والتي تحدد الاتجاه، يحتوي كل كائن أيضًا على مساحة الكائن الخاصة به مع مجموعة مستقلة من المحاور.
في حالة هذا النموذج، لم يتم إنشاؤه باستخدام ما نعتبره عادةً "أعلى" الدبوس الذي يواجه المحور y، لذا عليك تحويل العنصر لتوجيهه بالطريقة المطلوبة بالنسبة إلى مساحة العالم من خلال استدعاءrotation.set
عليه. يُرجى العِلم أنّه في Three.js، يتم تحديد الدوران بوحدات الراديان وليس بالدرجات. من الأسهل عمومًا التفكير بالدرجات، لذا يجب إجراء التحويل المناسب باستخدام الصيغةdegrees * Math.PI/180
.
بالإضافة إلى ذلك، النموذج صغير بعض الشيء، لذا عليك أيضًا تغيير حجمه بالتساوي على جميع المحاور من خلال استدعاءscale.set(x, y ,z)
.
لتدوير التصميم وتغيير حجمه، أضِف ما يلي فيloader
رد الاتصال الخاص بـonAdd
قبلscene.add(gltf.scene)
الذي يضيف ملف gLTF إلى المشهد:gltf.scene.scale.set(25,25,25); gltf.scene.rotation.x = 180 * Math.PI/180;
أصبح الدبوس الآن في وضع مستقيم بالنسبة إلى الخريطة.
يجب أن تبدو الآن خطافات onAdd
وonDraw
على النحو التالي:
webGLOverlayView.onAdd = () => {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera();
const ambientLight = new THREE.AmbientLight( 0xffffff, 0.75 ); // soft white light
scene.add( ambientLight );
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.25);
directionalLight.position.set(0.5, -1, 0.5);
scene.add(directionalLight);
loader = new GLTFLoader();
const source = 'pin.gltf';
loader.load(
source,
gltf => {
gltf.scene.scale.set(25,25,25);
gltf.scene.rotation.x = 180 * Math.PI/180;
scene.add(gltf.scene);
}
);
}
webGLOverlayView.onDraw = ({gl, transformer}) => {
const latLngAltitudeLiteral = {
lat: mapOptions.center.lat,
lng: mapOptions.center.lng,
altitude: 100
}
const matrix = transformer.fromLatLngAltitude(latLngAltitudeLiteral);
camera.projectionMatrix = new THREE.Matrix4().fromArray(matrix);
webGLOverlayView.requestRedraw();
renderer.render(scene, camera);
renderer.resetState();
}
بعد ذلك، سنتحدث عن الصور المتحركة للكاميرا.
8. تحريك الكاميرا
بعد عرض نموذج على الخريطة وإمكانية تحريك كل العناصر بشكل ثلاثي الأبعاد، من المحتمل أنّك تريد التحكّم في هذه الحركة آليًا. تتيح لك الدالة moveCamera
ضبط خصائص المركز والتكبير والإمالة والعنوان للخريطة في الوقت نفسه، ما يمنحك تحكّمًا دقيقًا في تجربة المستخدم. بالإضافة إلى ذلك، يمكن استدعاء moveCamera
في حلقة رسوم متحركة لإنشاء انتقالات سلسة بين اللقطات بمعدل لقطات يبلغ 60 لقطة في الثانية تقريبًا.
- انتظِر إلى أن يتم تحميل النموذج.
لإنشاء تجربة مستخدم سلسة، عليك الانتظار إلى أن يتم تحميل نموذج gLTF قبل البدء في تحريك الكاميرا. لإجراء ذلك، ألحِق معالج الأحداثonLoad
الخاص بأداة التحميل بالخطافonContextRestored
:loader.manager.onLoad = () => {}
- إنشاء حلقة صورة متحركة
هناك أكثر من طريقة لإنشاء حلقة صور متحركة، مثل استخدامsetInterval
أوrequestAnimationFrame
. في هذه الحالة، ستستخدِم الدالةsetAnimationLoop
في أداة العرض Three.js، والتي ستستدعي تلقائيًا أي رمز تعرّفه في دالة الاستدعاء كلّما عرضت Three.js إطارًا جديدًا. لإنشاء حلقة الحركة، أضِف ما يلي إلى معالج الحدثonLoad
في الخطوة السابقة:renderer.setAnimationLoop(() => {});
- اضبط موضع الكاميرا في حلقة الصور المتحركة.
بعد ذلك، اتّصِل برقمmoveCamera
لتعديل الخريطة. في ما يلي، يتم استخدام خصائص من العنصرmapOptions
الذي تم استخدامه لتحميل الخريطة لتحديد موضع الكاميرا:map.moveCamera({ "tilt": mapOptions.tilt, "heading": mapOptions.heading, "zoom": mapOptions.zoom });
- تعديل الكاميرا في كل إطار
الخطوة الأخيرة! عدِّل الكائنmapOptions
في نهاية كل إطار لضبط موضع الكاميرا للإطار التالي. في هذه الرمز البرمجي، يتم استخدام عبارةif
لزيادة الميل إلى أن يتم الوصول إلى قيمة الميل القصوى البالغة 67.5 درجة، ثم يتم تغيير العنوان قليلاً في كل إطار إلى أن تكمل الكاميرا دورانًا كاملاً بزاوية 360 درجة. بعد اكتمال الصورة المتحركة المطلوبة، يتم تمريرnull
إلىsetAnimationLoop
لإلغاء الصورة المتحركة حتى لا يتم تشغيلها إلى الأبد.if (mapOptions.tilt < 67.5) { mapOptions.tilt += 0.5 } else if (mapOptions.heading <= 360) { mapOptions.heading += 0.2; } else { renderer.setAnimationLoop(null) }
يجب أن يبدو خطاف onContextRestored
الآن على النحو التالي:
webGLOverlayView.onContextRestored = ({gl}) => {
renderer = new THREE.WebGLRenderer({
canvas: gl.canvas,
context: gl,
...gl.getContextAttributes(),
});
renderer.autoClear = false;
loader.manager.onLoad = () => {
renderer.setAnimationLoop(() => {
map.moveCamera({
"tilt": mapOptions.tilt,
"heading": mapOptions.heading,
"zoom": mapOptions.zoom
});
if (mapOptions.tilt < 67.5) {
mapOptions.tilt += 0.5
} else if (mapOptions.heading <= 360) {
mapOptions.heading += 0.2;
} else {
renderer.setAnimationLoop(null)
}
});
}
}
9- تهانينا
إذا سارت الأمور على ما يرام، من المفترض أن تظهر لك الآن خريطة تحتوي على دبوس ثلاثي الأبعاد كبير يبدو على النحو التالي:
ما تعلّمته
لقد تعلّمت الكثير في هذا الدرس العملي، وفي ما يلي أهم النقاط:
- تنفيذ
WebGLOverlayView
وخطافات دورة حياته - دمج Three.js في الخريطة
- أساسيات إنشاء مشهد Three.js، بما في ذلك الكاميرات والإضاءة
- تحميل التصاميم الثلاثية الأبعاد والتعامل معها باستخدام Three.js
- التحكّم في الكاميرا وتحريكها للخريطة باستخدام
moveCamera
ما هي الخطوات التالية؟
إنّ WebGL ورسومات الكمبيوتر بشكل عام موضوع معقّد، لذا هناك دائمًا الكثير لتعلّمه. في ما يلي بعض المراجع لمساعدتك في عملية الترقية:
- مستندات العرض المركّب باستخدام WebGL
- بدء استخدام WebGL
- مستندات Three.js
- يُرجى مساعدتنا في إنشاء المحتوى الذي تراه الأكثر فائدة من خلال الإجابة عن السؤال أدناه: «codelabs/maps-platform/shared/_next-lab-survey.lab.md» هل لم يتم إدراج برنامج التدريب العملي الذي تريده أعلاه؟ يمكنك طلب ذلك من خلال تقديم مشكلة جديدة هنا.