Создавайте трехмерные карты с помощью WebGL Overlay View.

1. Прежде чем начать

В этой лабораторной работе вы узнаете, как использовать функции Maps JavaScript API на основе WebGL для управления и визуализации векторной карты в трех измерениях.

Окончательный 3D-пин

Предпосылки

В этой лабораторной работе предполагается, что у вас есть промежуточные знания JavaScript и Maps JavaScript API. Чтобы изучить основы использования Maps JS API, попробуйте лабораторную работу Добавить карту на свой веб-сайт (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 , чтобы создать платежную учетную запись и проект.

  1. В Cloud Console щелкните раскрывающееся меню проекта и выберите проект, который вы хотите использовать для этой лаборатории кода.

  1. Включите API и SDK платформы Google Maps, необходимые для этой лаборатории кода, в Google Cloud Marketplace . Для этого выполните действия, описанные в этом видео или в этой документации .
  2. Создайте ключ API на странице учетных данных Cloud Console. Вы можете выполнить действия, описанные в этом видео или в этой документации . Для всех запросов к платформе Google Maps требуется ключ API.

Настройка Node.js

Если у вас его еще нет, перейдите на https://nodejs.org/ , чтобы загрузить и установить среду выполнения Node.js на свой компьютер.

Node.js поставляется с менеджером пакетов npm, который необходим для установки зависимостей для этой лаборатории кода.

Скачать стартовый шаблон проекта

Прежде чем приступить к работе с этой лабораторией кода, выполните следующие действия, чтобы загрузить шаблон начального проекта, а также полный код решения:

  1. Загрузите или разветвите репозиторий GitHub для этой кодовой лаборатории по адресу https://github.com/googlecodelabs/maps-platform-101-webgl/ . Стартовый проект находится в каталоге /starter и включает в себя базовую файловую структуру, необходимую для завершения лаборатории кода. Все, с чем вам нужно работать, находится в каталоге /starter/src .
  2. Как только вы загрузите начальный проект, запустите npm install в каталоге /starter . Это установит все необходимые зависимости, перечисленные в package.json .
  3. Как только ваши зависимости будут установлены, запустите npm start в каталоге.

Стартовый проект был настроен для использования вами webpack-dev-server, который компилирует и запускает код, который вы пишете локально. webpack-dev-server также автоматически перезагружает ваше приложение в браузере каждый раз, когда вы вносите изменения в код.

Если вы хотите увидеть работающий полный код решения, вы можете выполнить описанные выше действия по настройке в каталоге /solution .

Добавьте свой ключ API

Стартовое приложение включает в себя весь код, необходимый для загрузки карты с помощью JS API Loader , так что все, что вам нужно сделать, это предоставить ключ API и идентификатор карты. JS API Loader — это простая библиотека, которая абстрагирует традиционный метод загрузки JS API Карт, встроенный в HTML-шаблон с помощью тега script , что позволяет обрабатывать все в коде JavaScript.

Чтобы добавить ключ API, выполните следующие действия в стартовом проекте:

  1. Откройте app.js
  2. В объекте apiOptions задайте ключ API как значение apiOptions.apiKey .

3. Создайте и используйте идентификатор карты

Чтобы использовать функции Maps JavaScript API на основе WebGL, вам нужен идентификатор карты с включенной векторной картой.

Создание идентификатора карты

Создание идентификатора карты

  1. В консоли Google Cloud выберите «Платформа Google Maps» > «Управление картами».
  2. Нажмите «СОЗДАТЬ НОВЫЙ ID КАРТЫ».
  3. В поле «Название карты» введите имя для идентификатора вашей карты.
  4. В раскрывающемся списке «Тип карты» выберите «JavaScript». Появятся «Параметры JavaScript».
  5. В разделе «Параметры JavaScript» установите переключатель «Вектор», установите флажок «Наклон» и флажок «Поворот».
  6. Опционально . В поле «Описание» введите описание вашего ключа API.
  7. Нажмите кнопку «Далее». Появится страница «Подробности идентификатора карты».

    Страница сведений о карте
  8. Скопируйте идентификатор карты. Вы будете использовать это на следующем шаге для загрузки карты.

Использование идентификатора карты

Чтобы загрузить векторную карту, вы должны указать идентификатор карты в качестве свойства в параметрах при создании экземпляра карты. При желании вы также можете указать тот же идентификатор карты при загрузке Maps JavaScript API.

Чтобы загрузить карту с идентификатором карты, выполните следующие действия:

  1. Установите идентификатор карты в качестве значения mapOptions.mapId .

    Предоставление идентификатора карты при создании экземпляра карты сообщает платформе Google Maps, какие из ваших карт следует загружать для конкретного экземпляра. Вы можете повторно использовать один и тот же идентификатор карты в нескольких приложениях или в нескольких представлениях в одном приложении.
    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() . Это позволяет вам настроить все, что вам нужно, не добавляя лишних ресурсов к фактическому рендерингу карты, который и без того интенсивно использует GPU.
  • onDraw() : вызывается один раз за кадр, как только WebGL начинает рендеринг карты и всего, что вы запросили. Вы должны делать как можно меньше работы в onDraw() , чтобы избежать проблем с производительностью при рендеринге карты.
  • onContextLost() : вызывается, когда по какой-либо причине теряется контекст рендеринга WebGL.
  • onRemove() : вызывается, когда наложение удаляется с карты путем вызова setMap(null) в экземпляре WebGLOverlayView .

На этом шаге вы создадите экземпляр WebGLOverlayView и реализуете три его обработчика жизненного цикла: onAdd , onContextRestored и onDraw . Чтобы все было чисто и было проще для понимания, весь код для оверлея будет обрабатываться в функции initWebGLOverlayView() , представленной в начальном шаблоне для этой лаборатории кода.

  1. Создайте WebGLOverlayView() .

    Наложение предоставляется Maps JS API в google.maps.WebGLOverlayView . Для начала создайте экземпляр, добавив к initWebGLOverlayView() следующее:
    const webGLOverlayView = new google.maps.WebGLOverlayView();
    
  2. Реализуйте хуки жизненного цикла.

    Чтобы реализовать хуки жизненного цикла, добавьте к initWebGLOverlayView() следующее:
    webGLOverlayView.onAdd = () => {};
    webGLOverlayView.onContextRestored = ({gl}) => {};
    webGLOverlayView.onDraw = ({gl, coordinateTransformer}) => {};
    
  3. Добавьте экземпляр наложения на карту.

    Теперь вызовите setMap() для экземпляра оверлея и передайте карту, добавив к initWebGLOverlayView() следующее:
    webGLOverlayView.setMap(map)
    
  4. Вызовите 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 . Далее вы настроите все необходимое для рендеринга 3D-объекта на карте с помощью Three.js.

5. Настройте сцену three.js

Использование WebGL может быть очень сложным, поскольку требует от вас определения всех аспектов каждого объекта вручную, а затем и некоторых других. Чтобы упростить задачу, для этой кодовой лаборатории вы будете использовать Three.js, популярную графическую библиотеку, которая предоставляет упрощенный уровень абстракции поверх WebGL. Three.js поставляется с широким спектром удобных функций, которые делают все: от создания средства визуализации WebGL до рисования общих форм 2D- и 3D-объектов, управления камерами, преобразования объектов и многого другого.

В Three.js есть три основных типа объектов, которые необходимы для отображения чего-либо:

  • Сцена: «контейнер», в котором визуализируются и отображаются все объекты, источники света, текстуры и т. д.
  • Камера: камера, представляющая точку обзора сцены. Доступно несколько типов камер, и к одной сцене можно добавить одну или несколько камер.
  • Рендерер: визуализатор, который занимается обработкой и отображением всех объектов в сцене. В Three.js чаще всего используется WebGLRenderer , но несколько других доступны в качестве запасных вариантов на случай, если клиент не поддерживает WebGL.

На этом шаге вы загрузите все зависимости, необходимые для Three.js, и настроите базовую сцену.

  1. Загрузите файл three.js

    Для этой лаборатории кода вам понадобятся две зависимости: библиотека Three.js и загрузчик GLTF, класс, который позволяет загружать 3D-объекты в формате передачи GL (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';
    
    .
  2. Создайте сцену three.js.

    Чтобы создать сцену, создайте экземпляр класса Three.js Scene , добавив к onAdd :
    scene = new THREE.Scene();
    
  3. Добавьте камеру к сцене.

    Как упоминалось ранее, камера представляет перспективу просмотра сцены и определяет, как Three.js обрабатывает визуальную визуализацию объектов в сцене. Без камеры сцена фактически не «видна», а это означает, что объекты не будут отображаться, потому что они не будут визуализированы.

    Three.js предлагает множество различных камер, которые влияют на то, как средство визуализации обрабатывает объекты в отношении таких вещей, как перспектива и глубина. В этой сцене вы будете использовать PerspectiveCamera , наиболее часто используемый тип камеры в Three.js, который предназначен для имитации восприятия сцены человеческим глазом. Это означает, что объекты, находящиеся дальше от камеры, будут казаться меньше, чем объекты, расположенные ближе, сцена будет иметь точку схода и многое другое.

    Чтобы добавить PerspectiveCamera камеру в сцену, добавьте к onAdd
    camera = new THREE.PerspectiveCamera();
    
    : зрение (fov). В совокупности эти атрибуты составляют то, что известно как усеченная пирамида просмотра, важное понятие, которое необходимо понимать при работе в 3D, но оно выходит за рамки этой лаборатории кода. Конфигурации PerspectiveCamera по умолчанию будет вполне достаточно.
  4. Добавьте в сцену источники света.

    По умолчанию объекты, отображаемые в сцене Three.js, будут отображаться черными, независимо от примененных к ним текстур. Это связано с тем, что сцена Three.js имитирует поведение объектов в реальном мире, где видимость цвета зависит от света, отражающегося от объекта. Короче говоря, ни света, ни цвета.

    Three.js предоставляет множество различных типов источников света, из которых вы будете использовать два:

  5. AmbientLight : обеспечивает рассеянный источник света, который равномерно освещает все объекты в кадре со всех сторон. Это даст сцене базовое количество света, чтобы текстуры на всех объектах были видны.
  6. 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, вы, вероятно, знакомы с context HTML-холста, где все отображается. Возможно, вы не знаете, что это интерфейс, который предоставляет контекст рендеринга графики OpenGL через API WebGLRenderingContext в браузере.

Чтобы упростить работу с модулем рендеринга WebGL, Three.js предоставляет WebGLRenderer , оболочку, которая упрощает настройку контекста рендеринга WebGL, чтобы Three.js мог отображать сцены в браузере. Однако в случае с картой недостаточно просто отобразить сцену Three.js в браузере вместе с картой. Three.js должен отображаться в том же самом контексте рендеринга, что и карта, чтобы и карта, и любые объекты из сцены Three.js отображались в одном и том же мировом пространстве. Это позволяет рендереру обрабатывать взаимодействия между объектами на карте и объектами в сцене, такие как окклюзия, которая является причудливым способом сказать, что объект будет скрывать объекты за собой из поля зрения.

Звучит довольно сложно, правда? К счастью, Three.js снова приходит на помощь.

  1. Настройте визуализатор WebGL.

    Когда вы создаете новый экземпляр Three.js WebGLRenderer , вы можете предоставить ему конкретный контекст рендеринга WebGL, в котором вы хотите, чтобы он отображал вашу сцену. Помните аргумент gl , который передается в хук onContextRestored ? Этот объект gl является контекстом рендеринга карты WebGL. Все, что вам нужно сделать, это предоставить контекст, его холст и его атрибуты экземпляру WebGLRenderer , все из которых доступны через объект gl . В этом коде свойство autoClear средства визуализации также имеет значение false , чтобы средство визуализации не очищало свой вывод в каждом кадре.

    Чтобы настроить визуализатор, добавьте к onContextRestored :
    renderer = new THREE.WebGLRenderer({
      canvas: gl.canvas,
      context: gl,
      ...gl.getContextAttributes(),
    });
    renderer.autoClear = false;
    
  2. Рендеринг сцены.

    После того, как средство визуализации настроено, вызовите 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 .

  1. Создайте экземпляр загрузчика модели.

    Добавьте к onAdd следующее:
    loader = new GLTFLoader();
    
  2. Загрузите 3D-модель.

    Загрузчики моделей являются асинхронными и выполняют обратный вызов после полной загрузки модели. Чтобы загрузить pin.gltf , добавьте к onAdd следующее:
    const source = "pin.gltf";
    loader.load(
      source,
      gltf => {}
    );
    
  3. Добавьте модель на сцену.

    Теперь вы можете добавить модель на сцену, добавив следующее к обратному вызову loader . Обратите внимание, что добавляется gltf.scene , а не gltf :
    scene.add(gltf.scene);
    
  4. Настройте матрицу проекции камеры.

    Последнее, что вам нужно, чтобы правильно отобразить модель на карте, — это установить матрицу проекции камеры в сцене Three.js. Матрица проекции указывается в виде массива Three.js Matrix4 , который определяет точку в трехмерном пространстве вместе с преобразованиями, такими как повороты, сдвиг, масштаб и т. д.

    В случае WebGLOverlayView матрица проекции используется, чтобы указать средству визуализации, где и как отображать сцену Three.js относительно базовой карты. Но есть проблема. Местоположения на карте задаются парами координат широты и долготы, тогда как местоположения в сцене Three.js представляют собой координаты Vector3 . Как вы могли догадаться, вычисление преобразования между двумя системами не является тривиальным. Чтобы решить эту проблему, WebGLOverlayView передает объект coordinateTransformer Transformer в обработчик жизненного цикла OnDraw , который содержит функцию, вызываемую fromLatLngAltitude . fromLatLngAltitude принимает объект LatLngAltitude или LatLngAltitudeLiteral и, при необходимости, набор аргументов, определяющих преобразование сцены, а затем преобразует их в матрицу проекции представления модели (MVP) для вас. Все, что вам нужно сделать, это указать, где на карте вы хотите отобразить сцену Three.js, а также как вы хотите ее преобразовать, а WebGLOverlayView сделает все остальное. Затем вы можете преобразовать матрицу MVP в массив Three.js Matrix4 и установить для нее матрицу проекции камеры.

    В приведенном ниже коде второй аргумент указывает 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);
    
  5. Преобразуйте модель.

    Вы заметите, что булавка не расположена перпендикулярно карте. В 3D-графике, в дополнение к тому, что мировое пространство имеет собственные оси 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 кадров в секунду.

  1. Дождитесь загрузки модели.

    Чтобы создать беспрепятственный пользовательский интерфейс, вам нужно подождать, чтобы начать перемещение камеры, пока не будет загружена модель gLTF. Для этого добавьте обработчик события onContextRestored onLoad
    loader.manager.onLoad = () => {}
    
  2. Создайте цикл анимации.

    Существует несколько способов создания цикла анимации, например, с помощью setInterval или requestAnimationFrame . В этом случае вы будете использовать функцию setAnimationLoop средства визуализации Three.js, которая будет автоматически вызывать любой код, объявленный вами в своем обратном вызове, каждый раз, когда Three.js визуализирует новый кадр. Чтобы создать цикл анимации, добавьте в обработчик события onLoad на предыдущем шаге следующее:
    renderer.setAnimationLoop(() => {});
    
  3. Установите положение камеры в цикле анимации.

    Затем вызовите moveCamera , чтобы обновить карту. Здесь свойства объекта mapOptions , который использовался для загрузки карты, используются для определения положения камеры:
    map.moveCamera({
      "tilt": mapOptions.tilt,
      "heading": mapOptions.heading,
      "zoom": mapOptions.zoom
    });
    
  4. Обновляйте камеру каждый кадр.

    Последний шаг! Обновите объект 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-булавкой, которая выглядит так:

Окончательный 3D-пин

Что вы узнали

В этой кодлабе вы узнали кучу вещей; вот основные моменты:

  • Реализация WebGLOverlayView и его хуков жизненного цикла.
  • Интеграция Three.js в карту.
  • Основы создания сцены Three.js, включая камеры и освещение.
  • Загрузка и управление 3D-моделями с помощью Three.js.
  • Управление и анимация камеры для карты с помощью moveCamera .

Что дальше?

WebGL и компьютерная графика в целом — сложная тема, поэтому всегда есть чему поучиться. Вот несколько ресурсов, которые помогут вам начать: