תצוגת שכבת-על WebGL

לעיון בדוגמה

בעזרת תצוגת שכבת-העל של WebGL אפשר להוסיף תוכן למפות ישירות באמצעות WebGL, או באמצעות ספריות גרפיקה פופולריות כמו Three.js. תצוגת שכבת-על של WebGL מספקת גישה ישירה לאותו הקשר של עיבוד WebGL שבו נעשה שימוש בפלטפורמה של מפות Google כדי לעבד את מפת הבסיס של הווקטור. השימוש בהקשר עיבוד משותף מספק יתרונות כמו חסימה של עומק באמצעות גיאומטריה של מבנים תלת-ממדיים, והיכולת לסנכרן תוכן 2D/3D עם עיבוד של מפת בסיס. אפשר גם לקשר אובייקטים שעבר עיבוד באמצעות תצוגת שכבת-העל של WebGL לקואורדינטות של קו הרוחב או קו האורך, כך שהם ינועו כשגוררים את המפה, מגדילים אותה, מחליקים אותה או משעינים אותה.

דרישות

כדי להשתמש בתצוגת שכבת-על של WebGL, צריך לטעון את המפה באמצעות מזהה מפה שבו מפה וקטורית מופעלת. מומלץ מאוד להפעיל את ההטיה והסיבוב כשיוצרים את מזהה המפה, כדי לאפשר שליטה מלאה במצלמה בתלת-ממד. פרטים נוספים זמינים בסקירה הכללית.

הוספת שכבת-על מבוססת-WebGL

כדי להוסיף את שכבת-העל למפה, מטמיעים את google.maps.WebGLOverlayView ומעבירים לה את מופע המפה באמצעות setMap:

// Create a map instance.
const map = new google.maps.Map(mapDiv, mapOptions);

// Create a WebGL Overlay View instance.
const webglOverlayView = new google.maps.WebGLOverlayView();

// Add the overlay to the map.
webglOverlayView.setMap(map);

קטעי הוק (hooks) של מחזור חיים

שכבת-העל מבוססת-WebGL מספקת קבוצה של ווקים (hooks) שנקראים בזמנים שונים במחזור החיים של הקשר העיבוד של WebGL במפת הבסיס הוקטורית. ב-hooks האלה של מחזור החיים אפשר להגדיר, לצייר ולפרק כל מה שרוצים להציג בשכבת-העל.

  • onAdd() נקראת כשהשכבה העליונה נוצרת. אפשר להשתמש בו כדי לאחזר או ליצור מבני נתונים ביניים לפני שציור שכבת-העל מתחיל, בלי צורך בגישה מיידית להקשר העיבוד של WebGL.
  • onContextRestored({gl}) נקרא ברגע שמסגרת העיבוד זמינה. אפשר להשתמש בו כדי לאתחל או לקשר כל מצב של WebGL, כמו שידורים, אובייקטים של מאגר GL וכו'. onContextRestored() מקבלת מופע של WebGLStateOptions, שיש לו שדה אחד:
    • gl הוא כינוי ל-WebGLRenderingContext שבו משתמשת המפה הבסיסית.
  • onDraw({gl, transformer}) מבצעת עיבוד (רנדר) של הסצנה במפה הבסיסית. הפרמטרים של onDraw() הם אובייקט WebGLDrawOptions, שיש לו שני שדות:
    • gl הוא כינוי ל-WebGLRenderingContext שבו משתמשת המפה הבסיסית.
    • transformer מספק פונקציות עזר להמרה מקואורדינטות במפה למטריצה של מודל-תצוגה-השלכה, שאפשר להשתמש בה כדי לתרגם קואורדינטות במפה למרחב העולם, למרחב המצלמה ולמרחב המסך.
  • onContextLost() נקראת כשהקשר של העיבוד אובד מסיבה כלשהי, וזוהי הזדמנות לנקות את כל מצב GL קיים, כי אין בו יותר צורך.
  • onStateUpdate({gl}) מעדכן את מצב GL מחוץ ללולאת הרינדור, והוא מופעל כשקוראים ל-requestStateUpdate. הוא מקבל מופע WebGLStateOptions שיש לו שדה אחד:
    • gl הוא כינוי ל-WebGLRenderingContext שבו משתמשת המפה הבסיסית.
  • onRemove() נקראת כששכבת-העל מוסרת מהמפה באמצעות WebGLOverlayView.setMap(null), ושם צריך להסיר את כל האובייקטים הביניים.

לדוגמה, זוהי הטמעה בסיסית של כל האירועים במחזור החיים:

const webglOverlayView = new google.maps.WebGLOverlayView();

webglOverlayView.onAdd = () => {
  // Do setup that does not require access to rendering context.
}

webglOverlayView.onContextRestored = ({gl}) => {
  // Do setup that requires access to rendering context before onDraw call.
}

webglOverlayView.onStateUpdate = ({gl}) => {
  // Do GL state setup or updates outside of the render loop.
}

webglOverlayView.onDraw = ({gl, transformer}) => {
  // Render objects.
}

webglOverlayView.onContextLost = () => {
  // Clean up pre-existing GL state.
}

webglOverlayView.onRemove = () => {
  // Remove all intermediate objects.
}

webglOverlayView.setMap(map);

איפוס מצב GL

תצוגת שכבת-על מבוססת-WebGL חושפת את הקשר הרינדור של WebGL של מפת הבסיס. לכן חשוב מאוד לאפס את מצב GL למצב המקורי שלו אחרי שמסיימים את העיבוד של האובייקטים. אם לא תאפסו את מצב GL, סביר להניח שיהיו התנגשויות במצב GL, וכתוצאה מכך הרינדור של המפה ושל כל האובייקטים שציינתם ייכשל.

איפוס המצב של GL מתבצע בדרך כלל ב-hook של onDraw(). לדוגמה, ב-Three.js יש פונקציית עזר שמוחקת את כל השינויים במצב GL:

webglOverlayView.onDraw = ({gl, transformer}) => {
  // Specify an object to render.
  renderer.render(scene, camera);
  renderer.resetState();
}

אם לא ניתן ליצור רינדור של המפה או של האובייקטים, סביר מאוד שהמצב של GL לא אופס.

טרנספורמציות קואורדינטות

המיקום של אובייקט במפת הווקטורים מצוין על ידי שילוב של קואורדינטות רוחב ואורך, וגם גובה. עם זאת, גרפיקה תלת-ממדית מצוינה במרחב העולם, במרחב המצלמה או במרחב המסך. כדי שיהיה קל יותר להמיר קואורדינטות של מפה למרחבים האלה שבהם נעשה שימוש נרחב יותר, שכבת-העל מבוססת-WebGL מספקת את פונקציית העזר coordinateTransformer.fromLatLngAltitude(latLngAltitude, rotationArr, scalarArr) ב-hook‏ onDraw(), שמקבלת את הפרמטרים הבאים ומחזירה Float64Array:

  • latLngAltitude: קואורדינטות של קו רוחב/אורך/גובה בתור LatLngAltitude או LatLngAltitudeLiteral.
  • rotationArr: Float32Array של זוויות הסיבוב של אוילר שצוינו במעלות.
  • scalarArr: Float32Array של סקלריים להחלה על ציר הצפון-דרום.

לדוגמה, בקוד הבא נעשה שימוש ב-fromLatLngAltitude() כדי ליצור מטריצת הקרנה של מצלמה ב-Three.js:

const camera = new THREE.PerspectiveCamera();
const matrix = coordinateTransformer.fromLatLngAltitude({
    lat: mapOptions.center.lat,
    lng: mapOptions.center.lng,
    altitude: 120,
});
camera.projectionMatrix = new THREE.Matrix4().fromArray(matrix);

דוגמה

בדוגמה הבאה מוסבר איך להשתמש ב-Three.js, ספריית WebGL פופולרית בקוד פתוח, כדי להציב אובייקט תלת-ממדי במפה. בקודלאב הזה מוסבר בפירוט איך משתמשים בשכבת-על מבוססת-WebGL כדי ליצור את הדוגמה שמוצגת בחלק העליון של הדף הזה.

const webglOverlayView = new google.maps.WebGLOverlayView();
let scene, renderer, camera, loader;

webglOverlayView.onAdd = () => {
  // Set up the Three.js scene.
  scene = new THREE.Scene();
  camera = new THREE.PerspectiveCamera();
  const ambientLight = new THREE.AmbientLight( 0xffffff, 0.75 ); // Soft white light.
  scene.add(ambientLight);

  // Load the 3D model with GLTF Loader from Three.js.
  loader = new GLTFLoader();
  loader.load("pin.gltf");
}

webglOverlayView.onContextRestored = ({gl}) => {
  // Create the Three.js renderer, using the
  // maps's WebGL rendering context.
  renderer = new THREE.WebGLRenderer({
    canvas: gl.canvas,
    context: gl,
    ...gl.getContextAttributes(),
  });
  renderer.autoClear = false;
}

webglOverlayView.onDraw = ({gl, transformer}) => {
  // Update camera matrix to ensure the model is georeferenced correctly on the map.
  const matrix = transformer.fromLatLngAltitude({
      lat: mapOptions.center.lat,
      lng: mapOptions.center.lng,
      altitude: 120,
  });
camera.projectionMatrix = new THREE.Matrix4().fromArray(matrix);

  // Request a redraw and render the scene.
  webglOverlayView.requestRedraw();
  renderer.render(scene, camera);

  // Always reset the GL state.
  renderer.resetState();
}

// Add the overlay to the map.
webglOverlayView.setMap(map);