1. ก่อนเริ่มต้น
Codelab นี้จะสอนวิธีใช้ฟีเจอร์ที่ทำงานด้วย WebGL ของ Maps JavaScript API เพื่อควบคุมและแสดงผลบนแผนที่เวกเตอร์ในรูปแบบ 3 มิติ
ข้อกำหนดเบื้องต้น
Codelab นี้ถือว่าคุณมีความรู้ระดับกลางเกี่ยวกับ JavaScript และ Maps JavaScript API หากต้องการเรียนรู้พื้นฐานการใช้ Maps JS API ให้ลองใช้ Codelab เพิ่มแผนที่ลงในเว็บไซต์ (JavaScript)
สิ่งที่คุณจะได้เรียนรู้
- สร้างรหัสแผนที่โดยเปิดใช้แผนที่เวกเตอร์สำหรับ JavaScript
- ควบคุมแผนที่ด้วยการเอียงและการหมุนแบบเป็นโปรแกรม
- การแสดงผลออบเจ็กต์ 3 มิติบนแผนที่ด้วย
WebGLOverlayView
และ Three.js - การทำภาพเคลื่อนไหวการเคลื่อนกล้องด้วย
moveCamera
สิ่งที่คุณต้องมี
- บัญชี Google Cloud Platform ที่เปิดใช้การเรียกเก็บเงิน
- คีย์ API ของ 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 ให้คลิกเมนูแบบเลื่อนลงของโปรเจ็กต์ แล้วเลือกโปรเจ็กต์ที่ต้องการใช้สำหรับ Codelab นี้
- เปิดใช้ Google Maps Platform APIs และ SDK ที่จำเป็นสำหรับ Codelab นี้ใน Google Cloud Marketplace โดยทำตามขั้นตอนในวิดีโอนี้หรือเอกสารประกอบนี้
- สร้างคีย์ API ในหน้าข้อมูลเข้าสู่ระบบของ Cloud Console คุณสามารถทำตามขั้นตอนในวิดีโอนี้หรือเอกสารประกอบนี้ คำขอทั้งหมดไปยัง Google Maps Platform ต้องใช้คีย์ API
การตั้งค่า Node.js
หากยังไม่มี ให้ไปที่ https://nodejs.org/ เพื่อดาวน์โหลดและติดตั้งรันไทม์ Node.js ในคอมพิวเตอร์
Node.js มาพร้อมกับตัวจัดการแพ็กเกจ npm ซึ่งคุณต้องติดตั้งการอ้างอิงสำหรับ Codelab นี้
ดาวน์โหลดเทมเพลตเริ่มต้นโปรเจ็กต์
ก่อนที่จะเริ่ม Codelab นี้ ให้ทำดังนี้เพื่อดาวน์โหลดเทมเพลตโปรเจ็กต์เริ่มต้น รวมถึงโค้ดโซลูชันที่สมบูรณ์
- ดาวน์โหลดหรือแยกที่เก็บ GitHub สำหรับ Codelab นี้ได้ที่ https://github.com/googlecodelabs/maps-platform-101-webgl/ โปรเจ็กต์เริ่มต้นจะอยู่ในไดเรกทอรี
/starter
และมีโครงสร้างไฟล์พื้นฐานที่คุณต้องใช้เพื่อทำ Codelab ให้เสร็จสมบูรณ์ ทุกอย่างที่คุณต้องใช้ในการทำงานจะอยู่ในไดเรกทอรี/starter/src
- เมื่อดาวน์โหลดโปรเจ็กต์เริ่มต้นแล้ว ให้เรียกใช้
npm install
ในไดเรกทอรี/starter
ซึ่งจะติดตั้งการอ้างอิงที่จำเป็นทั้งหมดที่ระบุไว้ในpackage.json
- เมื่อติดตั้งการขึ้นต่อกันแล้ว ให้เรียกใช้
npm start
ในไดเรกทอรี
เราได้ตั้งค่าโปรเจ็กต์เริ่มต้นให้คุณใช้ webpack-dev-server ซึ่งจะคอมไพล์และเรียกใช้โค้ดที่คุณเขียนในเครื่อง นอกจากนี้ webpack-dev-server ยังโหลดแอปของคุณในเบราว์เซอร์ซ้ำโดยอัตโนมัติทุกครั้งที่คุณทำการเปลี่ยนแปลงโค้ด
หากต้องการดูโค้ดโซลูชันแบบเต็มที่ทำงานอยู่ ให้ทำตามขั้นตอนการตั้งค่าด้านบนในไดเรกทอรี /solution
เพิ่มคีย์ API
แอปเริ่มต้นมีโค้ดทั้งหมดที่จำเป็นในการโหลดแผนที่ด้วย JS API Loader ดังนั้นสิ่งที่คุณต้องทำคือระบุคีย์ API และรหัสแผนที่ JS API Loader เป็นไลบรารีที่เรียบง่ายซึ่งแยกวิธีการโหลด Maps JS API แบบอินไลน์ในเทมเพลต HTML แบบเดิมด้วยแท็ก script
ทำให้คุณจัดการทุกอย่างในโค้ด JavaScript ได้
หากต้องการเพิ่มคีย์ API ให้ทำดังนี้ในโปรเจ็กต์เริ่มต้น
- เปิด
app.js
- ในออบเจ็กต์
apiOptions
ให้ตั้งค่าคีย์ API เป็นค่าของapiOptions.apiKey
3. สร้างและใช้รหัสแผนที่
หากต้องการใช้ฟีเจอร์ที่ใช้ WebGL ของ Maps JavaScript API คุณต้องมีรหัสแผนที่ที่เปิดใช้แผนที่เวกเตอร์
การสร้างรหัสแผนที่
- ในคอนโซล Google Cloud ให้ไปที่ "Google Maps Platform" > "การจัดการแผนที่"
- คลิก "สร้างรหัสแผนที่ใหม่"
- ในช่อง "ชื่อแผนที่" ให้ป้อนชื่อรหัสแผนที่
- ในเมนูแบบเลื่อนลง "ประเภทแผนที่" ให้เลือก "JavaScript" "ตัวเลือก JavaScript" จะปรากฏขึ้น
- ในส่วน "ตัวเลือก JavaScript" ให้เลือกปุ่มตัวเลือก "เวกเตอร์" ช่องทําเครื่องหมาย "เอียง" และช่องทําเครื่องหมาย "หมุน"
- ไม่บังคับ ในช่อง "คำอธิบาย" ให้ป้อนคำอธิบายสำหรับคีย์ API
- คลิกปุ่ม "ถัดไป" หน้า "รายละเอียดรหัสแผนที่" จะปรากฏขึ้น
- คัดลอกรหัสแผนที่ คุณจะต้องใช้ข้อมูลนี้ในขั้นตอนถัดไปเพื่อโหลดแผนที่
การใช้รหัสแผนที่
หากต้องการโหลดแผนที่เวกเตอร์ คุณต้องระบุรหัสแผนที่เป็นพร็อพเพอร์ตี้ในตัวเลือกเมื่อสร้างอินสแตนซ์ของแผนที่ นอกจากนี้ คุณยังระบุรหัสแผนที่เดียวกันเมื่อโหลด Maps JavaScript API ได้ด้วย
หากต้องการโหลดแผนที่ด้วยรหัสแมป ให้ทำดังนี้
- ตั้งรหัสแผนที่เป็นค่าของ
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 เดียวกันที่ใช้ในการแสดงผลแผนที่ฐานแบบเวกเตอร์ได้โดยตรง ซึ่งหมายความว่าคุณสามารถแสดงผลออบเจ็กต์ 2 มิติและ 3 มิติบนแผนที่ได้โดยตรงโดยใช้ WebGL รวมถึงไลบรารีกราฟิกยอดนิยมที่ใช้ WebGL
WebGLOverlayView
แสดงฮุก 5 รายการในวงจรของบริบทการแสดงผล WebGL ของแผนที่ที่คุณใช้ได้ ต่อไปนี้คือคำอธิบายโดยย่อของแต่ละฮุกและสิ่งที่คุณควรใช้
onAdd()
: เรียกใช้เมื่อเพิ่มการวางซ้อนลงในแผนที่โดยการเรียกsetMap
ในอินสแตนซ์WebGLOverlayView
คุณควรทำงานที่เกี่ยวข้องกับ WebGL ซึ่งไม่จำเป็นต้องเข้าถึงบริบท WebGL โดยตรงในส่วนนี้onContextRestored()
: เรียกใช้เมื่อบริบท WebGL พร้อมใช้งาน แต่ก่อนที่จะมีการแสดงผลใดๆ คุณควรเริ่มต้นออบเจ็กต์ ผูกสถานะ และทำสิ่งอื่นๆ ที่ต้องเข้าถึงบริบท WebGL แต่สามารถทำได้นอกการเรียกonDraw()
ซึ่งจะช่วยให้คุณตั้งค่าทุกอย่างที่ต้องการได้โดยไม่ต้องเพิ่มค่าใช้จ่ายที่ไม่จำเป็นในการแสดงผลแผนที่จริง ซึ่งใช้ GPU เป็นจำนวนมากอยู่แล้วonDraw()
: เรียกใช้ 1 ครั้งต่อเฟรมเมื่อ WebGL เริ่มแสดงผลแผนที่และสิ่งอื่นๆ ที่คุณขอ คุณควรทำงานในonDraw()
ให้น้อยที่สุดเพื่อหลีกเลี่ยงปัญหาด้านประสิทธิภาพในการแสดงผลแผนที่onContextLost()
: เรียกใช้เมื่อบริบทการแสดงผล WebGL หายไปไม่ว่าด้วยเหตุผลใดก็ตามonRemove()
: เรียกใช้เมื่อนำภาพซ้อนทับออกจากแผนที่โดยการเรียกใช้setMap(null)
ในอินสแตนซ์WebGLOverlayView
ในขั้นตอนนี้ คุณจะสร้างอินสแตนซ์ของ WebGLOverlayView
และใช้ฮุกวงจร 3 รายการ ได้แก่ onAdd
, onContextRestored
และ onDraw
เพื่อให้โค้ดสะอาดและติดตามได้ง่ายขึ้น เราจะจัดการโค้ดทั้งหมดสำหรับการซ้อนทับในฟังก์ชัน initWebGLOverlayView()
ที่มีให้ในเทมเพลตเริ่มต้นสำหรับโค้ดแล็บนี้
- สร้างอินสแตนซ์
WebGLOverlayView()
การวางซ้อนนี้มาจาก Maps JS API ในgoogle.maps.WebGLOverlayView
หากต้องการเริ่มต้น ให้สร้างอินสแตนซ์โดยเพิ่มข้อความต่อไปนี้ในinitWebGLOverlayView()
const webGLOverlayView = new google.maps.WebGLOverlayView();
- ใช้ Hook สำหรับวงจร
หากต้องการใช้ฮุกวงจร ให้ต่อท้ายข้อความต่อไปนี้ใน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
จากนั้นคุณจะตั้งค่าทุกอย่างที่จำเป็นในการแสดงผลออบเจ็กต์ 3 มิติบนแผนที่โดยใช้ Three.js
5. ตั้งค่าฉาก three.js
การใช้ WebGL อาจซับซ้อนมากเนื่องจากคุณต้องกำหนดทุกแง่มุมของออบเจ็กต์ทั้งหมดด้วยตนเองและอื่นๆ เพื่อความสะดวกยิ่งขึ้น คุณจะใช้ Three.js ใน Codelab นี้ ซึ่งเป็นไลบรารีกราฟิกยอดนิยมที่มีเลเยอร์การแยกส่วนที่เรียบง่ายอยู่เหนือ WebGL Three.js มาพร้อมฟังก์ชันอำนวยความสะดวกที่หลากหลายซึ่งทำทุกอย่างตั้งแต่การสร้างโปรแกรมแสดงผล WebGL ไปจนถึงการวาดรูปร่างออบเจ็กต์ 2 มิติและ 3 มิติทั่วไป ไปจนถึงการควบคุมกล้อง การเปลี่ยนรูปแบบออบเจ็กต์ และอื่นๆ อีกมากมาย
Three.js มีออบเจ็กต์พื้นฐาน 3 ประเภทที่จำเป็นต่อการแสดงผล
- ฉาก: "คอนเทนเนอร์" ที่แสดงผลและแสดงออบเจ็กต์ แหล่งกำเนิดแสง พื้นผิว ฯลฯ ทั้งหมด
- กล้อง: กล้องที่แสดงมุมมองของฉาก มีกล้องหลายประเภทให้เลือกใช้ และคุณอาจเพิ่มกล้องอย่างน้อย 1 ตัวลงในฉากเดียวได้
- โปรแกรมแสดงผล: โปรแกรมแสดงผลที่จัดการการประมวลผลและการแสดงออบเจ็กต์ทั้งหมดในฉาก ใน Three.js
WebGLRenderer
เป็นรูปแบบที่ใช้กันมากที่สุด แต่ก็มีรูปแบบอื่นๆ อีก 2-3 รูปแบบที่ใช้เป็นตัวสำรองในกรณีที่ไคลเอ็นต์ไม่รองรับ WebGL
ในขั้นตอนนี้ คุณจะโหลดการอ้างอิงทั้งหมดที่จำเป็นสำหรับ Three.js และตั้งค่าฉากพื้นฐาน
- โหลด three.js
คุณจะต้องมี 2 การอ้างอิงสำหรับโค้ดแล็บนี้ ได้แก่ ไลบรารี Three.js และ GLTF Loader ซึ่งเป็นคลาสที่ช่วยให้คุณโหลดออบเจ็กต์ 3 มิติใน GL Transmission Format (gLTF) ได้ Three.js มีโปรแกรมโหลดเฉพาะสำหรับออบเจ็กต์ 3 มิติในรูปแบบต่างๆ มากมาย แต่เราขอแนะนำให้ใช้ gLTF
ในโค้ดด้านล่าง ระบบจะนำเข้าไลบรารี Three.js ทั้งหมด ในแอปเวอร์ชันที่ใช้งานจริง คุณอาจต้องการนำเข้าเฉพาะคลาสที่ต้องการ แต่สำหรับ Codelab นี้ ให้นำเข้าทั้งไลบรารีเพื่อให้ง่าย นอกจากนี้ โปรดทราบว่า GLTF Loader ไม่ได้รวมอยู่ในไลบรารีเริ่มต้น และต้องนำเข้าจากเส้นทางแยกต่างหากใน Dependency ซึ่งเป็นเส้นทางที่คุณสามารถเข้าถึงตัวโหลดทั้งหมดที่ 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) คุณลักษณะเหล่านี้รวมกันเป็นสิ่งที่เรียกว่าฟรัสตัมของการดู ซึ่งเป็นแนวคิดสำคัญที่ต้องทำความเข้าใจเมื่อทำงานในระบบ 3 มิติ แต่ไม่อยู่ในขอบเขตของโค้ดแล็บนี้ การกำหนดค่าPerspectiveCamera
เริ่มต้นก็เพียงพอแล้ว - เพิ่มแหล่งกำเนิดแสงลงในฉาก
โดยค่าเริ่มต้น ออบเจ็กต์ที่เรนเดอร์ในฉาก Three.js จะปรากฏเป็นสีดำ ไม่ว่าจะมีพื้นผิวใดก็ตาม เนื่องจากฉาก Three.js จะจำลองวิธีที่ออบเจ็กต์ทำงานในโลกแห่งความจริง ซึ่งการมองเห็นสีจะขึ้นอยู่กับแสงที่สะท้อนออกจากออบเจ็กต์ กล่าวโดยสรุปคือไม่มีแสงก็ไม่มีสี
Three.js มีแสงประเภทต่างๆ มากมาย ซึ่งคุณจะใช้ 2 ประเภท ได้แก่ AmbientLight
: ให้แหล่งกำเนิดแสงแบบกระจายที่ส่องสว่างวัตถุทั้งหมดในฉากอย่างสม่ำเสมอจากทุกมุม ซึ่งจะทำให้ฉากมีปริมาณแสงพื้นฐานเพื่อให้มั่นใจว่าพื้นผิวของออบเจ็กต์ทั้งหมดจะมองเห็นได้DirectionalLight
: ให้แสงที่มาจากทิศทางในฉาก รังสีของแสงที่ออกมาจากDirectionalLight
จะขนานกันทั้งหมดและไม่กระจายตัวเมื่ออยู่ห่างจากแหล่งกำเนิดแสง ซึ่งแตกต่างจากลักษณะการทำงานของแสงที่วางตำแหน่งในโลกแห่งความเป็นจริง
คุณสามารถกำหนดค่าสีและความเข้มของไฟแต่ละดวงเพื่อสร้างเอฟเฟกต์แสงโดยรวม ตัวอย่างเช่น ในโค้ดด้านล่าง แสงแวดล้อมจะให้แสงสีขาวนวลสำหรับทั้งฉาก ในขณะที่แสงแบบกำหนดทิศทางจะให้แสงรองที่ส่องไปยังออบเจ็กต์ในมุมลง ในกรณีของแสงแบบมีทิศทาง มุมจะตั้งค่าโดยใช้position.set(x, y ,z)
โดยแต่ละค่าจะสัมพันธ์กับแกนที่เกี่ยวข้อง เช่นposition.set(0,1,0)
จะวางตำแหน่งแสงเหนือฉากโดยตรงบนแกน y ซึ่งชี้ลงมาตรงๆ
หากต้องการเพิ่มแหล่งกำเนิดแสงลงในฉาก ให้เพิ่มข้อมูลต่อไปนี้ลงใน HookonAdd
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 แสดงผลเนื้อหา 2 มิติและ 3 มิติในเบราว์เซอร์โดยใช้ Canvas API หากเคยใช้ Canvas API มาก่อน คุณอาจคุ้นเคยกับ context
ของ Canvas HTML ซึ่งเป็นที่ที่แสดงผลทุกอย่าง สิ่งที่คุณอาจไม่ทราบคืออินเทอร์เฟซนี้จะแสดงบริบทการแสดงผลกราฟิก OpenGL ผ่าน WebGLRenderingContext
API ในเบราว์เซอร์
Three.js มี WebGLRenderer
ซึ่งเป็น Wrapper ที่ช่วยให้กำหนดค่าบริบทการแสดงผล WebGL ได้ง่ายขึ้น เพื่อให้ Three.js แสดงผลฉากในเบราว์เซอร์ได้ แต่ในกรณีของแผนที่ การแสดงผลฉาก Three.js ในเบราว์เซอร์ควบคู่ไปกับแผนที่เพียงอย่างเดียวนั้นไม่เพียงพอ Three.js ต้องแสดงผลในบริบทการแสดงผลเดียวกันกับแผนที่ เพื่อให้ทั้งแผนที่และออบเจ็กต์จากฉาก Three.js แสดงผลในพื้นที่โลกเดียวกัน ซึ่งช่วยให้โปรแกรมแสดงผลจัดการการโต้ตอบระหว่างออบเจ็กต์บนแผนที่กับออบเจ็กต์ในฉากได้ เช่น การบดบัง ซึ่งเป็นวิธีที่ซับซ้อนในการบอกว่าออบเจ็กต์จะซ่อนออบเจ็กต์ที่อยู่ด้านหลังไม่ให้มองเห็น
ฟังดูซับซ้อนใช่ไหม โชคดีที่ Three.js กลับมาช่วยอีกครั้ง
- ตั้งค่าโปรแกรมแสดงผล WebGL
เมื่อสร้างอินสแตนซ์ใหม่ของ Three.jsWebGLRenderer
คุณอาจระบุบริบทการแสดงผล WebGL ที่ต้องการให้แสดงผลฉาก จำgl
อาร์กิวเมนต์ที่ส่งไปยังฮุกonContextRestored
ได้ไหมgl
ออบเจ็กต์ดังกล่าวคือบริบทการแสดงผล WebGL ของแผนที่ สิ่งที่คุณต้องทำคือระบุบริบท Canvas และแอตทริบิวต์ของบริบทนั้นไปยังอินสแตนซ์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 ต้องอาศัยสถานะ GL ที่แชร์ หากไม่ได้รีเซ็ตสถานะเมื่อสิ้นสุดการเรียกใช้การวาดทุกครั้ง ความขัดแย้งของสถานะ GL อาจทำให้ตัวเรนเดอร์ทำงานไม่สำเร็จ
หากต้องการดำเนินการนี้ ให้ต่อท้ายonDraw
Hook ด้วยโค้ดต่อไปนี้เพื่อให้โค้ดทำงานในแต่ละเฟรม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. แสดงโมเดล 3 มิติบนแผนที่
โอเค ตอนนี้คุณก็มีชิ้นส่วนทั้งหมดพร้อมแล้ว คุณได้ตั้งค่ามุมมองภาพซ้อนทับ WebGL และสร้างฉาก Three.js แล้ว แต่มีปัญหาอย่างหนึ่งคือไม่มีอะไรอยู่ในฉาก ดังนั้นต่อไปก็ถึงเวลาแสดงผลออบเจ็กต์ 3 มิติในฉาก โดยคุณจะต้องใช้ GLTF Loader ที่นำเข้าก่อนหน้านี้
โมเดล 3 มิติมีหลายรูปแบบ แต่สำหรับ Three.js รูปแบบ gLTF เป็นรูปแบบที่แนะนำเนื่องจากมีขนาดและประสิทธิภาพรันไทม์ที่ดี ใน Codelab นี้ เราได้จัดเตรียมโมเดลให้คุณแสดงผลในฉากไว้ใน /src/pin.gltf
แล้ว
- สร้างอินสแตนซ์โปรแกรมโหลดโมเดล
เพิ่มข้อความต่อไปนี้ลงในonAdd
loader = new GLTFLoader();
- โหลดโมเดล 3 มิติ
ตัวโหลดโมเดลเป็นแบบอะซิงโครนัสและจะเรียกใช้ Callback เมื่อโหลดโมเดลเสร็จสมบูรณ์ หากต้องการโหลด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
อย่างที่คุณอาจคาดเดาได้ การคำนวณ Conversion ระหว่าง 2 ระบบนี้ไม่ใช่เรื่องง่าย หากต้องการแก้ไขปัญหานี้WebGLOverlayView
จะส่งออบเจ็กต์coordinateTransformer
ไปยังฮุกวงจรOnDraw
ที่มีฟังก์ชันชื่อfromLatLngAltitude
fromLatLngAltitude
รับออบเจ็กต์LatLngAltitude
หรือLatLngAltitudeLiteral
และอาร์กิวเมนต์ชุดหนึ่ง (ไม่บังคับ) ที่กำหนดการเปลี่ยนรูปสำหรับฉาก จากนั้นจะแปลงออบเจ็กต์เหล่านั้นเป็นเมทริกซ์การฉายภาพมุมมองโมเดล (MVP) ให้คุณ สิ่งที่คุณต้องทำคือระบุตำแหน่งบนแผนที่ที่ต้องการให้แสดงผลฉาก Three.js รวมถึงวิธีที่ต้องการให้แปลง และWebGLOverlayView
จะจัดการส่วนที่เหลือให้ จากนั้นคุณสามารถแปลงเมทริกซ์ MVP เป็นอาร์เรย์ Three.jsMatrix4
และตั้งค่าเมทริกซ์การฉายภาพของกล้องเป็นเมทริกซ์ดังกล่าว
ในโค้ดด้านล่าง อาร์กิวเมนต์ที่ 2 จะบอกให้ WebGl Overlay View ตั้งค่าความสูงของฉาก Three.js ที่ 120 เมตรเหนือพื้นดิน ซึ่งจะทำให้โมเดลดูเหมือนลอยอยู่
หากต้องการตั้งค่าเมทริกซ์การฉายภาพของกล้อง ให้ต่อท้ายข้อความต่อไปนี้ใน HookonDraw
const latLngAltitudeLiteral = { lat: mapOptions.center.lat, lng: mapOptions.center.lng, altitude: 120 } const matrix = transformer.fromLatLngAltitude(latLngAltitudeLiteral); camera.projectionMatrix = new THREE.Matrix4().fromArray(matrix);
- เปลี่ยนรูปแบบโมเดล
คุณจะเห็นว่าหมุดไม่ได้ตั้งฉากกับแผนที่ ในกราฟิก 3 มิติ นอกเหนือจากพื้นที่ว่างของโลกที่มีแกน x, y และ z ของตัวเองซึ่งกำหนดการวางแนวแล้ว วัตถุแต่ละชิ้นยังมีพื้นที่ว่างของวัตถุของตัวเองที่มีชุดแกนอิสระด้วย
ในกรณีของโมเดลนี้ โมเดลไม่ได้สร้างขึ้นโดยมีสิ่งที่ปกติเราจะถือว่าเป็น "ด้านบน" ของหมุดหันขึ้นแกน y ดังนั้นคุณจึงต้องเปลี่ยนออบเจ็กต์เพื่อจัดวางในลักษณะที่ต้องการเทียบกับพื้นที่โลกโดยเรียกใช้rotation.set
บนออบเจ็กต์ โปรดทราบว่าใน Three.js การหมุนจะระบุเป็นเรเดียน ไม่ใช่องศา โดยทั่วไปแล้วการคิดเป็นองศาจะง่ายกว่า ดังนั้นจึงต้องทำการแปลงที่เหมาะสมโดยใช้สูตรdegrees * Math.PI/180
นอกจากนี้ โมเดลยังมีขนาดเล็ก คุณจึงต้องปรับขนาดให้เท่ากันในทุกแกนโดยเรียกใช้scale.set(x, y ,z)
หากต้องการหมุนและปรับขนาดโมเดล ให้เพิ่มโค้ดต่อไปนี้ในloader
การเรียกกลับของonAdd
beforescene.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. สร้างภาพเคลื่อนไหวของกล้อง
ตอนนี้คุณได้เรนเดอร์โมเดลบนแผนที่และย้ายทุกอย่างแบบ 3 มิติแล้ว สิ่งต่อไปที่คุณน่าจะอยากทำคือควบคุมการเคลื่อนไหวด้วยโปรแกรม ฟังก์ชัน moveCamera
ช่วยให้คุณตั้งค่าพร็อพเพอร์ตี้ของศูนย์กลาง การซูม การเอียง และส่วนหัวของแผนที่ได้พร้อมกัน ซึ่งจะช่วยให้คุณควบคุมประสบการณ์ของผู้ใช้ได้อย่างละเอียด นอกจากนี้ คุณยังเรียกใช้ moveCamera
ในลูปภาพเคลื่อนไหวเพื่อสร้างการเปลี่ยนภาพที่ราบรื่นระหว่างเฟรมที่อัตราเฟรมเกือบ 60 เฟรมต่อวินาทีได้ด้วย
- รอให้โมเดลโหลด
หากต้องการสร้างประสบการณ์ของผู้ใช้ที่ราบรื่น คุณควรรอจนกว่าระบบจะโหลดโมเดล gLTF เสร็จแล้วจึงเริ่มย้ายกล้อง โดยให้ต่อท้ายonLoad
เครื่องจัดการเหตุการณ์ของโปรแกรมโหลดเข้ากับฮุกonContextRestored
ดังนี้loader.manager.onLoad = () => {}
- สร้างลูปภาพเคลื่อนไหว
การสร้างภาพเคลื่อนไหวแบบวนซ้ำทำได้หลายวิธี เช่น ใช้setInterval
หรือrequestAnimationFrame
ในกรณีนี้ คุณจะใช้ฟังก์ชันsetAnimationLoop
ของตัวแสดงผล Three.js ซึ่งจะเรียกใช้โค้ดที่คุณประกาศใน Callback โดยอัตโนมัติทุกครั้งที่ 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. ขอแสดงความยินดี
หากทุกอย่างเป็นไปตามแผน ตอนนี้คุณควรมีแผนที่ที่มีหมุด 3 มิติขนาดใหญ่ซึ่งมีลักษณะดังนี้
สิ่งที่คุณได้เรียนรู้
ในโค้ดแล็บนี้ คุณได้เรียนรู้สิ่งต่างๆ มากมายแล้ว ต่อไปนี้คือไฮไลต์
- การติดตั้งใช้งาน
WebGLOverlayView
และ Hook สำหรับวงจร - การผสานรวม Three.js เข้ากับแผนที่
- พื้นฐานของการสร้างฉาก Three.js รวมถึงกล้องและแสง
- การโหลดและจัดการโมเดล 3 มิติโดยใช้ Three.js
- การควบคุมและสร้างภาพเคลื่อนไหวของกล้องสำหรับแผนที่โดยใช้
moveCamera
ขั้นตอนถัดไปคือ
WebGL และกราฟิกคอมพิวเตอร์โดยทั่วไปเป็นหัวข้อที่ซับซ้อน จึงมีสิ่งต่างๆ ให้เรียนรู้อยู่เสมอ แหล่งข้อมูลที่จะช่วยในการเริ่มต้นใช้งานมีดังนี้
- เอกสารประกอบเกี่ยวกับมุมมองการวางซ้อนของ WebGL
- การเริ่มต้นใช้งาน WebGL
- เอกสารประกอบของ Three.js
- ช่วยเราสร้างเนื้อหาที่เป็นประโยชน์ต่อคุณมากที่สุดโดยตอบคำถามด้านล่าง «codelabs/maps-platform/shared/_next-lab-survey.lab.md» หากไม่พบ Codelab ที่ต้องการในรายการด้านบน ขอได้โดยแจ้งปัญหาใหม่ที่นี่