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

הצגת דוגמה

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

דרישות

כדי להשתמש בתצוגת שכבת-על של 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 מספקת קבוצה של ווים שמופעלים בזמנים שונים במחזור החיים של הקשר העיבוד של WebGL במפת הבסיס הווקטורית. בנקודות החיבור האלה למחזור החיים מגדירים, מציירים ומפרקים את כל מה שרוצים להציג בשכבת-העל.

  • onAdd() מופעל כשיוצרים את שכבת העל. אפשר להשתמש בו כדי לאחזר או ליצור מבני נתונים ביניים לפני שהשכבה השקופה מצוירת, שלא דורשים גישה מיידית להקשר העיבוד של WebGL.
  • הפונקציה onContextRestored({gl}) מופעלת ברגע שהקשר של העיבוד זמין. אפשר להשתמש בו כדי לאתחל או לקשר כל מצב WebGL, כמו shaders, אובייקטים של GL buffer וכו'. ‫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), וזה המקום שבו צריך להסיר את כל האובייקטים הביניים.

לדוגמה, ההטמעה הבסיסית הבאה כוללת את כל ה-lifecycle hooks:

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 לא אופס.

Coordinate Transformations

המיקום של אובייקט במפת הווקטור נקבע על ידי צירוף של קואורדינטות של קו רוחב וקו אורך, וגם גובה. לעומת זאת, גרפיקה תלת-ממדית מוגדרת במרחב העולמי, במרחב המצלמה או במרחב המסך. כדי להקל על המרת קואורדינטות של מפה למרחבים האלה שנמצאים בשימוש נפוץ יותר, שכבת העל מבוססת-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 לבניית הדוגמה שמוצגת בחלק העליון של הדף הזה, אפשר לנסות את הסדנה הדיגיטלית בנושא בניית חוויות מפה מואצות באמצעות 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);