1. לפני שמתחילים
שיעור Lab זה מלמד איך להשתמש בתכונות המופעלות על ידי WebGL של ה-API של מפות Google JavaScript כדי לשלוט ולעבד במפה הווקטורית בשלושה מאפיינים.
דרישות מוקדמות
ערכת ה-Codelab הזו מבוססת על הידע שלכם ב-JavaScript וב-API של מפות Google. לקבלת מידע בסיסי על השימוש ב-API של מפות JS, נסו את הוספת מפה לאתר שלכם (קוד JavaScript).
מה תלמדו
- יצירת מזהה מפה עם מפה וקטורית עבור JavaScript מופעלת.
- שליטה במפה באמצעות הטיה פרוגרמטית וסיבוב.
- רינדור אובייקטים בתלת-ממד במפה באמצעות
WebGLOverlayView
ו-Three.js. - אנימציית תנועות המצלמה באמצעות
moveCamera
.
מה תצטרך להכין
- חשבון ב-Google Cloud Platform עם חיוב מופעל
- מפתח API של פלטפורמת מפות Google עם ממשק API של JavaScript במפות Google מופעל
- ידע בינוני ב-JavaScript, ב-HTML וב-CSS
- עורך טקסט או IDE לבחירתך
- Node.js
2. להגדרה
בשלב ההפעלה הבא, יש להפעיל את API של מפות Google.
הגדרת מפות Google
אם עדיין אין לכם חשבון Google Cloud Platform ופרויקט שבו מופעל חיוב, כדאי לעיין במדריך תחילת העבודה עם הפלטפורמה של מפות Google ליצירת חשבון לחיוב ופרויקט.
- ב-Cloud Console, לוחצים על התפריט הנפתח של הפרויקט ובוחרים את הפרויקט שבו רוצים להשתמש ב-Codelab הזה.
- מפעילים את ממשקי ה-API ואת ערכות ה-SDK של מפות Google הנדרשים למעבדת קוד זו ב-Google Cloud Marketplace. כדי לעשות זאת, יש לבצע את השלבים המפורטים בסרטון הזה או בתיעוד הזה.
- יוצרים מפתח API בדף פרטי הכניסה ב-Cloud Console. ניתן לבצע את השלבים המפורטים בסרטון הזה או בתיעוד הזה. לכל הבקשות שנשלחות לפלטפורמה של מפות Google נדרש מפתח API.
הגדרת Node.js
אם עדיין לא עשיתם זאת, נכנסים לכתובת https://nodejs.org/ כדי להוריד ולהתקין את זמן הריצה של Node.js במחשב.
Node.js מגיע עם מנהל חבילת npm, שצריך להתקין יחסי תלות עבור מעבדת קוד זו.
הורדת התבנית של פרויקט למתחילים
לפני שמתחילים את ה-Codelab הזה, צריך לבצע את הפעולות הבאות כדי להוריד את התבנית של הפרויקט למתחילים, וגם את קוד הפתרון המלא:
- אפשר להוריד את ה-Repo RepoGveb או לממש אותו במעבדה זו בכתובת 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
.
הוספת מפתח API
האפליקציה למתחילים כוללת את כל הקוד שנדרש לטעינת המפה באמצעות JS API Loader, כך שכל מה שאתם צריכים לעשות הוא לספק את מפתח ה-API ואת מזהה המפה שלכם. JS API Loader הוא ספרייה פשוטה שמפשטת את השיטה המסורתית של טעינת ממשק API של JS API בתוך תבנית ה-HTML עם תג script
, ומאפשרת לך לטפל בהכול בקוד JavaScript.
כדי להוסיף את מפתח ה-API, יש לבצע את הפעולות הבאות בפרויקט למתחילים:
- פתיחת
app.js
- באובייקט
apiOptions
, מגדירים את מפתח ה-API כערך שלapiOptions.apiKey
.
3. יצירת מזהה מפה ושימוש בו
כדי להשתמש בתכונות המבוססות על WebGL של ה-API של JavaScript במפות, נדרש מזהה מפה שבו מופעלת מפת וקטור.
יצירת מזהה מפה
- במסוף Google Cloud, עוברים אל 'מפות Google מפות' >'ניהול מפות'.
- לוחצים על 'יצירת מזהה מפה חדש'.
- בשדה 'שם המפה'', מזינים שם למזהה המפה.
- בתפריט הנפתח 'סוג מפה&339; בוחרים באפשרות 'JavaScript'. ‘אפשרויות JavaScript' יופיעו.
- בקטע 'אפשרויות JavaScript', בוחרים בתיבת הסימון 'וקטור', תיבת הסימון 'הטיה' ותיבת הסימון 'סיבוב''
- Optional. בשדה ‘תיאור&33’, מזינים תיאור של מפתח ה-API שלכם.
- לוחצים על הלחצן 'הבא'. הדף 'פרטי מזהה מפה' יופיע.
- מעתיקים את מזהה המפה. הקוד ישמש אתכם בשלב הבא לטעינת המפה.
שימוש במזהה מפה
כדי לטעון את המפה הווקטורית, יש לספק מזהה מפה כנכס באפשרויות של יצירת המפה. אפשר גם לספק את אותו מזהה מפה כשטוענים את API של JavaScript.
כדי לטעון את המפה באמצעות מזהה המפה, מבצעים את הפעולות הבאות:
- יש להגדיר את מזהה המפה כערך של
mapOptions.mapId
.
מתן מזהה המפה בעת יצירת המפה אומרת ל-Google Maps Platform אילו מהמפות שלך לטעון עבור מופע מסוים. ניתן להשתמש באותו מזהה מפה במספר אפליקציות או בתצוגות מרובות באותה אפליקציה.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()
. כך תוכלו להגדיר את כל מה שאתם צריכים בלי להוסיף תקורה מיותרת לעיבוד בפועל של המפה, שכבר נעשה בה שימוש ב-GPU.onDraw()
: מתבצעת קריאה פעם אחת לכל מסגרת אחרי ש-WebGL מתחיל לעבד את המפה וכל דבר אחר שביקשת. עליך לעבוד מעט ככל האפשר בonDraw()
כדי למנוע בעיית ביצועים בעיבוד המפה.onContextLost()
: מתבצעת כשהקשר של WebGL אבד מסיבה כלשהי.onRemove()
: מתבצעת קריאה כששכבת-העל תוסר מהמפה על ידי קריאה ל-setMap(null)
במופעWebGLOverlayView
.
בשלב זה, עליכם ליצור מופע של WebGLOverlayView
ולהטמיע שלושה ווים במחזור החיים: onAdd
, onContextRestored
ו-onDraw
. כדי לשמור על תוכן נקי ונוח למעקב, כל הקוד של שכבת-העל יעובד בפונקציה initWebGLOverlayView()
שסופקה בתבנית למתחילים של מעבדת הקוד הזו.
- יצירת מופע אחד (
WebGLOverlayView()
).
שכבת-העל מסופקת על ידי Maps JS API ב-google.maps.WebGLOverlayView
. כדי להתחיל, יש ליצור מכונה על ידי המתנה לקטע הבא:initWebGLOverlayView()
:const webGLOverlayView = new google.maps.WebGLOverlayView();
- הטמעת ווים למחזור חיים.
כדי להטמיע את הווים למחזור החיים, יש לצרף את הפרמטרים הבאים ל-initWebGLOverlayView()
:webGLOverlayView.onAdd = () => {}; webGLOverlayView.onContextRestored = ({gl}) => {}; webGLOverlayView.onDraw = ({gl, coordinateTransformer}) => {};
- מוסיפים למפה את מופע שכבת-העל.
יש להתקשר עכשיו אל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, coordinateTransformer}) => {}
webGLOverlayView.setMap(map);
}
(async () => {
const map = await initMap();
initWebGLOverlayView(map);
})();
זה כל מה שצריך לעשות כדי להטמיע את WebGLOverlayView
. בשלב הבא, עליך להגדיר את כל מה שדרוש לך כדי לעבד אובייקט תלת-ממדי במפה באמצעות Three.js.
5. הגדרה של סצנת Tri.js
השימוש ב-WebGL יכול להיות מסובך מאוד, כי כדי להשתמש בו צריך להגדיר את כל ההיבטים של כל אובייקט באופן ידני, ואז את חלקם. כדי להקל על התהליך, אתם יכולים להשתמש ב-Three.js, ספריית גרפיקה פופולרית המציעה שכבת הפשטה פשוטה בחלק העליון של WebGL. Three.js מגיע עם מגוון רחב של פונקציות נוחות שמבצעות כל מיני פעולות, מיצירת רינדור של WebGL ועד ציור צורות אובייקט דו-ממדיות ותלת-ממדיות ועד שליטה במצלמות, טרנספורמציות אובייקטים ועוד הרבה יותר.
ב-Three.js יש שלושה סוגי אובייקטים בסיסיים הנדרשים להצגת כל תוכן:
- סצנה: "קונטיינר" כאשר כל האובייקטים, מקורות התאורה, המרקמים וכו' מעובדים ומוצגים.
- מצלמה: מצלמה שמייצגת את נקודת המבט של הסצנה. קיימים מספר סוגי מצלמות זמינים, וייתכן שמצלמה אחת או יותר יתווספו לסצנה יחידה.
- רינדור: כלי עיבוד שמטפל בעיבוד ובהצגה של כל האובייקטים בסצינה. ב-Three.js,
WebGLRenderer
הוא הנפוץ ביותר, אך יש עוד כמה כלים שמאפשרים גיבוי במקרה שהלקוח לא תומך ב-WebGL.
בשלב זה, טוענים את כל התלות הנדרשות ב-Three.js ומגדירים סצנה בסיסית.
- טעינת שלוש.js
אתה זקוק ל-2 יחסי תלות ב-codelab זה: הספרייה Three.js ו-GLTF Loader, מחלקה שמאפשרת לטעון אובייקטים תלת-ממדיים בפורמט העברה של GL (gLTF). Three.js מציע מטענים מיוחדים בפורמטים רבים של אובייקט תלת-ממדי, אבל מומלץ להשתמש ב-gLTF.
בקוד הבא מתבצע ייבוא של כל ספריית Three.js. באפליקציית ייצור, סביר להניח שתרצו לייבא רק את הכיתות הדרושות לכם, אבל ב-Codelab הזה אפשר לייבא את כל הספרייה כדי שהתהליך יהיה פשוט. כמו כן, יש לשים לב שכלי הטעינה של GLTF לא נכלל בספריית ברירת המחדל, וצריך לייבא אותו מנתיב נפרד מהתלויות - זהו הנתיב שממנו אפשר לגשת לכל המטענים שסופקו על ידי Three.js.
כדי לייבא את האפליקציה Three.js ל-GLTF Loader, יש להוסיף את הפרטים הבאים לראש הדףapp.js
:import * as THREE from 'three'; import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js';
- ליצור סצינה 3.js.
כדי ליצור סצנה, יש להחפיץ את המחלקה Three.jsScene
על ידי הוספת הפריט הבא ב-onAdd
:webhookscene = new THREE.Scene();
- מוסיפים מצלמה לסצנה.
כפי שצוין קודם לכן, המצלמה מייצגת את נקודת המבט של הסצנה והיא קובעת את אופן הטיפול של Three.js בהדמיה של אובייקטים בסצנה. ללא מצלמה, למעשה התמונה לא מוחזרת; כלומר אובייקטים לא יופיעו כי הם לא יעובדו.
לאתר Three.js יש מגוון מצלמות שמשפיעות על האופן שבו אפשר לעבד את האובייקטים בהקשרים כמו פרספקטיבה ועומק. בסצנה הזו אתם משתמשים ב-PerspectiveCamera
, סוג המצלמה הנפוץ ביותר ב-Three.js, שנועד לדמות את הדרך שבה העין האנושית תזהה את התמונה. כלומר, אובייקטים רחוקים יותר מהמצלמה נראים קטנים יותר מאובייקטים קרובים יותר, סצנת הצילום תיעלם ועוד.
כדי להוסיף מצלמת פרספקטיבה לסצנה, צריך להוסיף את המאפיינים הבאים לווקינגonAdd
:
באמצעותcamera = new THREE.PerspectiveCamera();
PerspectiveCamera
, אפשר גם להגדיר את המאפיינים שמהם מורכבת נקודת המבט, כולל המטוסים הקרובים והרחוקים, שדה הראייה ושדה הראייה (פוב). יחד, המאפיינים האלה מהווים את מה שמכונה תפיסה: תפיסה חשובה שצריך להבין בעת עבודה בתלת-ממד, אבל מחוץ להיקף של שיעור Lab זה. הגדרת ברירת המחדל של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. אם השתמשתם בעבר ב-Canvas API, אתם בוודאי מכירים את context
של הקנבס של HTML, שבו הכול מעובד. ייתכן שלא ידוע לכם שזה ממשק שחושף את ההקשר של עיבוד הגרפיקה ב-OpenGL דרך ה-API של WebGLRenderingContext
בדפדפן.
כדי להקל על הטיפול ברינדור WebGL, Three.js מספק WebGLRenderer
, wrapper שמקל באופן כללי על הגדרת ההקשר של 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
ברינדור ולעבור אותו לסצנת ה-שלוש.js והמצלמה כדי לעבד. לבסוף, מנקים את מצב ההקשר של WebGL. זהו שלב חשוב למניעת התנגשויות בין מצבי GL, מכיוון שהשימוש בתצוגת שכבת-על של WebGL מסתמך על מצב 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 ויצרת סצנת Three.js, אבל יש בעיה אחת: אין בה כלום. אז הגיע הזמן לעבד אובייקט תלת-ממדי בסצנה. לשם כך, עליך להשתמש ב-GLTF Loader שייבאת בעבר.
דגמים תלת-ממדיים זמינים במגוון פורמטים, אך בפורמט Three.js הפורמט gLTF הוא הפורמט המועדף בשל הגודל שלו וביצועי זמן הריצה. במעבדה זו, כבר קיים ב-/src/pin.gltf
מודל שמתאים להצגה בסצנה.
- יצירת מופע של הכלי לטעינת דגם.
יש להוסיף את הטקסט הבא אלonAdd
:loader = new GLTFLoader();
- טעינת מודל תלת-ממדי.
טוענים את הדגמים הם אסינכרוניים ומבצעים קריאה חוזרת (callback) לאחר שהמודל נטען במלואו. כדי לטעון אתpin.gltf
, יש לצרף את הטקסט הבא אלonAdd
:const source = "pin.gltf"; loader.load( source, gltf => {} );
- מוסיפים את המודל לסצנה.
עכשיו אפשר להוסיף את המודל לסצנה על ידי צירוף הפרמטר הבא לקריאה חוזרת (callback) ב-loader
. חשוב להוסיף אתgltf.scene
, ולא אתgltf
:scene.add(gltf.scene);
- הגדרת המטריצה של המצלמה.
הדבר האחרון שצריך לעשות כדי שהדגם יעובד כראוי במפה הוא להגדיר את מטריצת ההקרנה של המצלמה בסצנת Three.js. מטריצת התחזית מוגדרת כמערך של שלוש.jsMatrix4
, שמגדיר נקודה בשלושה מימדים בשילוב עם טרנספורמציות, כמו סיבובים, חיתוך, קנה מידה ועוד.
במקרה שלWebGLOverlayView
, אנחנו משתמשים במטריצת ההקרנה כדי לציין לגורם הרינדור איפה ואיך לעבד את סצנת Three.js ביחס למפת הבסיס. אבל יש בעיה. המיקומים במפה מסומנים כצמדים של קו אורך וקו רוחב, ואילו המיקומים בסצנת Three.js הם קואורדינטותVector3
. כפי שוודאי ניחשת, חישוב ההמרה בין שתי המערכות אינו טריוויאלי. כדי לפתור את הבעיה הזו,WebGLOverlayView
מעביר אובייקטcoordinateTransformer
אל הובלה במחזורOnDraw
שמכילה פונקציה שנקראתfromLatLngAltitude
.fromLatLngAltitude
משתמש באובייקטLatLngAltitude
אוLatLngAltitudeLiteral
, ובאופן אופציונלי גם קבוצת ארגומנטים שמגדירה טרנספורמציה לסצנה, ולאחר מכן מכסה אותם במטריצה של תצוגת מודל (MVP) עבורך. כל מה שאתם צריכים לעשות הוא לציין איפה אתם רוצים שסביבת ה-Three.js תופיע במפה, ואיך אתם רוצים שהיא תשתנה.WebGLOverlayView
נעשה את כל השאר. לאחר מכן אפשר להמיר את מטריצת ה-MVP למערךMatrix4
של Three.js ולהגדיר בו את מטריצת ההקרנה של המצלמה.
בקוד שלמטה, הארגומנט השני מורה לתצוגת שכבת-על באינטרנט של WebGL להגדיר את הגובה של סצנת 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);
- משנים את המודל.
ייתכן שהסיכה לא תמוקם לרוחב המפה. בגרפיקה בתלת-ממד, בנוסף למרחב משותף, יש ציר X, y ו-z שקובעים את הכיוון, לכל אובייקט יש מרחב אובייקטים משלו עם צירים עצמאיים.
במקרה של המודל הזה, הוא לא נוצר עם מה שנחשב בדרך כלל כ'למעלה ו-#39'; של הסיכה שפונה כלפי ציר ה-y. לכן, צריך לסובב את האובייקט כדי לכוון אותו לכיוון הרצוי ביחס למרחב המשותף. לשם כך, יש להפעיל את הערךrotation.set
. הערה: ב-Three.js, הסיבוב מצוין ברדיאנים, ולא במעלות. בדרך כלל קל יותר לחשוב על מעלות. לכן צריך לבצע את ההמרה המתאימה באמצעות הנוסחהdegrees * Math.PI/180
.
כמו כן, המודל קטן יחסית, כך שגם אם תתקשרו ל-scale.set(x, y ,z)
תוכלו להרחיב אותו באופן שווה בכל הצירים.
כדי לסובב את המודל ולשנות את קנה המידה שלו, יש להוסיף את האפשרויות הבאות בקריאה החוזרת (callback) של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. כדי לעשות זאת, יש להוסיף את ה-handler של האירוע שלonLoad
לכלי הטעינהonContextRestored
:loader.manager.onLoad = () => {}
- יצירת לולאת אנימציה.
יש יותר מדרך אחת ליצור לולאת אנימציה, כמו שימוש ב-setInterval
או ב-requestAnimationFrame
. במקרה כזה, תשתמש בפונקציהsetAnimationLoop
של הרינדור Three.js, שתתקשר באופן אוטומטי לכל קוד שתכריש בקריאה החוזרת שלו בכל פעם ש-Three.js יבצע עיבוד של מסגרת חדשה. כדי ליצור את לולאת האנימציה, יש להוסיף את הדברים הבאים ל-handler של האירוע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.
- תיעוד של Tri.js
- כדי לעזור לנו ליצור את התוכן השימושי ביותר, אפשר לענות על השאלה הבאה: «codelabs/maps-platform/shared/_Next-lab-survey.md» האם קוד הגישה שאתם רוצים לא מופיע למעלה? אפשר לבקש אותו באמצעות בעיה חדשה כאן.