1. Прежде чем начать
В этой лабораторной работе вы научитесь использовать функции API JavaScript Карт на базе WebGL для управления и визуализации векторной карты в трех измерениях.
Предпосылки
Эта практическая работа предполагает наличие у вас промежуточных знаний JavaScript и API JavaScript для Карт. Чтобы изучить основы использования API JavaScript для Карт, попробуйте выполнить практическую работу «Добавление карты на ваш сайт (JavaScript)» .
Чему вы научитесь
- Генерация идентификатора карты с включенной векторной картой для JavaScript.
- Управление картой с помощью программного наклона и поворота.
- Рендеринг 3D-объектов на карте с помощью
WebGLOverlayView
и Three.js . - Анимация движения камеры с помощью
moveCamera
.
Что вам понадобится
- Учетная запись Google Cloud Platform с включенным выставлением счетов
- Ключ API платформы Google Карт с включенным API JavaScript Карт
- Среднее знание JavaScript, HTML и CSS
- Текстовый редактор или IDE по вашему выбору
- Node.js
2. Настройте
Для выполнения шага включения, описанного ниже, вам необходимо включить Maps JavaScript API .
Настройте платформу Google Карт
Если у вас еще нет учетной записи Google Cloud Platform и проекта с включенным выставлением счетов, ознакомьтесь с руководством « Начало работы с Google Maps Platform», чтобы создать учетную запись для выставления счетов и проект.
- В Cloud Console щелкните раскрывающееся меню проектов и выберите проект, который вы хотите использовать для этой кодовой лаборатории.
- Включите API и SDK платформы Google Карт, необходимые для этой лабораторной работы, в Google Cloud Marketplace . Для этого следуйте инструкциям в этом видео или в этой документации .
- Сгенерируйте ключ API на странице «Учётные данные» в Cloud Console. Вы можете следовать инструкциям в этом видео или в этой документации . Для всех запросов к платформе Google Карт требуется ключ API.
Настройка 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
.
Добавьте свой ключ API
Стартовое приложение включает в себя весь код, необходимый для загрузки карты с помощью JS API Loader , так что вам нужно лишь указать свой ключ API и идентификатор карты. JS API Loader — это простая библиотека, которая абстрагирует традиционный метод загрузки Maps JS API, встроенного в HTML-шаблон, с помощью тега script
, позволяя обрабатывать все данные в коде JavaScript.
Чтобы добавить свой ключ API, выполните следующие действия в стартовом проекте:
- Откройте
app.js
- В объекте
apiOptions
задайте свой ключ API как значениеapiOptions.apiKey
.
3. Создайте и используйте идентификатор карты.
Чтобы использовать функции JavaScript API Карт на базе WebGL, вам необходим идентификатор карты с включенной векторной картой.
Генерация идентификатора карты
- В консоли Google Cloud перейдите в раздел «Платформа Google Карт» > «Управление картами».
- Нажмите «СОЗДАТЬ НОВЫЙ ИДЕНТИФИКАТОР КАРТЫ».
- В поле «Название карты» введите имя для идентификатора вашей карты.
- В раскрывающемся списке «Тип карты» выберите «JavaScript». Появятся «Параметры JavaScript».
- В разделе «Параметры JavaScript» выберите переключатель «Вектор», установите флажки «Наклон» и «Вращение».
- Необязательно . В поле «Описание» введите описание вашего ключа API.
- Нажмите кнопку «Далее». Откроется страница «Сведения об идентификаторе карты».
- Скопируйте идентификатор карты. Он понадобится вам на следующем этапе для загрузки карты.
Использование идентификатора карты
Чтобы загрузить векторную карту, необходимо указать идентификатор карты в качестве свойства в параметрах при создании экземпляра карты. При желании вы также можете указать тот же идентификатор карты при загрузке 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, который используется для рендеринга векторной базовой карты. Это означает, что вы можете рендерить 2D- и 3D-объекты непосредственно на карте, используя 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 JS 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
. Далее вам нужно настроить всё необходимое для визуализации 3D-объекта на карте с помощью Three.js.
5. Настройте сцену three.js
Использование WebGL может быть очень сложным, поскольку требует ручного определения всех аспектов каждого объекта, и даже больше. Чтобы упростить задачу, в этой лабораторной работе вы будете использовать Three.js — популярную графическую библиотеку, предоставляющую упрощённый уровень абстракции поверх WebGL. Three.js содержит широкий набор удобных функций, которые выполняют всё: от создания рендерера WebGL до отрисовки распространённых форм 2D- и 3D-объектов, управления камерами, преобразованиями объектов и многим другим.
В Three.js есть три основных типа объектов, которые необходимы для отображения чего-либо:
- Сцена: «контейнер», в котором визуализируются и отображаются все объекты, источники света, текстуры и т. д.
- Камера: камера, представляющая точку обзора сцены. Доступны несколько типов камер, и к одной сцене можно добавить одну или несколько камер.
- Рендерер: Рендерер, отвечающий за обработку и отображение всех объектов на сцене. В Three.js чаще всего используется
WebGLRenderer
, но есть и другие варианты в качестве запасного варианта на случай, если клиент не поддерживает WebGL.
На этом этапе вы загрузите все зависимости, необходимые для Three.js, и настроите базовую сцену.
- Загрузить three.js
Для этой лабораторной работы вам понадобятся две зависимости: библиотека Three.js и загрузчик GLTF, класс, позволяющий загружать 3D-объекты в формате GL Trasmission Format (gLTF) . Three.js предлагает специализированные загрузчики для множества различных форматов 3D-объектов, но рекомендуется использовать gLTF.
В приведённом ниже коде импортируется вся библиотека Three.js. В рабочем приложении вы, вероятно, захотите импортировать только необходимые классы, но в этой лабораторной работе для простоты импортируем всю библиотеку. Обратите внимание, что загрузчик GLTF не входит в библиотеку по умолчанию и должен быть импортирован из отдельного пути в зависимости — по этому пути вы можете получить доступ ко всем загрузчикам, предоставляемым Three.js.
Чтобы импортировать Three.js и загрузчик GLTF, добавьте следующее в началоapp.js
:import * as THREE from 'three'; import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js';
- Создайте сцену three.js.
Чтобы создать сцену, создайте экземпляр класса Three.jsScene
, добавив к хукуonAdd
следующее:scene = new THREE.Scene();
- Добавьте камеру к сцене.
Как упоминалось ранее, камера представляет собой перспективу обзора сцены и определяет, как Three.js обрабатывает визуальный рендеринг объектов в сцене. Без камеры сцена фактически «не видна», то есть объекты не будут отображаться, поскольку не будут визуализироваться.
Three.js предлагает множество различных камер, которые влияют на то, как рендерер обрабатывает объекты с учётом таких факторов, как перспектива и глубина. В этой сцене вы будете использоватьPerspectiveCamera
— наиболее распространённый тип камеры в Three.js, разработанный для эмуляции восприятия сцены человеческим глазом. Это означает, что объекты, расположенные дальше от камеры, будут казаться меньше, чем объекты, расположенные ближе, у сцены появится точка схода и многое другое.
Чтобы добавить перспективную камеру к сцене, добавьте к хукуonAdd
следующее: С помощьюcamera = new THREE.PerspectiveCamera();
PerspectiveCamera
вы также можете настроить атрибуты, определяющие точку обзора, включая ближнюю и дальнюю плоскости, соотношение сторон и поле зрения (fov). В совокупности эти атрибуты образуют так называемую пирамиду видимости (visual frustum) — важную концепцию, которую необходимо понимать при работе с 3D, но которая выходит за рамки данной практической работы. Конфигурации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 рендерит 2D- и 3D-контент в браузере с помощью API Canvas . Если вы уже использовали API Canvas, вы, вероятно, знакомы с context
HTML-холста, в котором всё и отображается. Возможно, вы не знали, что это интерфейс, который предоставляет контекст рендеринга графики OpenGL через API 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. Отобразить 3D-модель на карте
Итак, всё готово. Вы настроили WebGl Overlay View и создали сцену Three.js, но есть одна проблема: в ней ничего нет. Теперь пора визуализировать 3D-объект в сцене. Для этого воспользуйтесь импортированным ранее загрузчиком GLTF.
3D-модели доступны во множестве различных форматов, но для Three.js формат gLTF является предпочтительным благодаря его размеру и производительности. В этой практической работе модель для рендеринга в сцене уже предоставлена в файле /src/pin.gltf
.
- Создайте экземпляр загрузчика модели.
Добавьте кonAdd
следующее:loader = new GLTFLoader();
- Загрузите 3D-модель.
Загрузчики моделей работают асинхронно и выполняют обратный вызов после полной загрузки модели. Чтобы загрузить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
и, при необходимости, набор аргументов, определяющих преобразование для сцены, а затем преобразует их в матрицу проекции вида модели (MVP) . Все, что вам нужно сделать, это указать, где на карте вы хотите визуализировать сцену Three.js, а также как вы хотите ее преобразовать, иWebGLOverlayView
сделает все остальное. Затем вы можете преобразовать матрицу MVP в массив Three.jsMatrix4
и задать для него матрицу проекции камеры.
В коде ниже второй аргумент сообщает 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);
- Трансформируйте модель.
Вы заметите, что булавка не перпендикулярна карте. В трёхмерной графике, помимо того, что мировое пространство имеет собственные оси X, Y и Z, определяющие ориентацию, каждый объект также имеет своё собственное объектное пространство с независимым набором осей.
В случае этой модели она не была создана с тем, что мы обычно считаем «верхней частью» штифта, обращённой вверх по оси 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 градусов. После завершения анимации вsetAnimationLoop
передаётсяnull
для её отмены, чтобы анимация не выполнялась бесконечно.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. Поздравления
Если все прошло по плану, у вас должна получиться карта с большой 3D-меткой, которая выглядит примерно так:
Что вы узнали
В этой лабораторной работе вы узнали много нового. Вот основные моменты:
- Реализация
WebGLOverlayView
и его хуков жизненного цикла. - Интеграция Three.js в карту.
- Основы создания сцены Three.js, включая камеры и освещение.
- Загрузка и манипулирование 3D-моделями с помощью Three.js.
- Управление и анимация камеры для карты с помощью
moveCamera
.
Что дальше?
WebGL, как и компьютерная графика в целом, — сложная тема, поэтому всегда есть чему поучиться. Вот несколько ресурсов для начала:
- Наложение WebGL Просмотреть документацию
- Начало работы с WebGL .
- Документация Three.js
- Помогите нам создать контент, который будет вам наиболее полезен, ответив на вопрос ниже: «codelabs/maps-platform/shared/_next-lab-survey.lab.md». Нужная вам кодлаб-программа не указана выше? Запросите её в новом выпуске здесь .