Оптимизация 3D-данных с помощью сжатия геометрии Draco

1. Обзор

3D-графика является фундаментальной частью многих приложений, включая игры, дизайн и визуализацию данных. По мере совершенствования графических процессоров и инструментов для создания контента, более крупные и сложные 3D-модели станут обычным явлением и будут способствовать развитию новых приложений в области иммерсивной виртуальной реальности (VR) и дополненной реальности (AR). Из-за возросшей сложности моделей требования к хранению данных и пропускной способности должны соответствовать стремительному росту объёма 3D-данных.

Благодаря Draco, приложения, использующие 3D-графику, могут быть значительно меньше по размеру без ущерба для качества изображения. Для пользователей это означает, что приложения теперь можно загружать быстрее, 3D-графика в браузере загружается быстрее, а сцены VR и AR теперь могут передаваться с гораздо меньшей пропускной способностью, быстро отображаться и выглядеть фантастически.

Кто такой Драко?

Draco — это библиотека для сжатия и распаковки трехмерных геометрических сеток и облаков точек . Она предназначена для улучшения хранения и передачи трехмерной графики.

Draco был разработан и создан для обеспечения эффективности и скорости сжатия. Код поддерживает сжатие точек, информации о связности, текстурных координат, информации о цвете, нормалей и любых других общих атрибутов, связанных с геометрией. Draco выпускается в виде исходного кода на C++, который можно использовать для сжатия 3D-графики, а также в виде декодеров на C++ и Javascript для закодированных данных.

Что вы узнаете

  • Как использовать Draco для сжатия 3D-модели
  • Как использовать различные модели сжатия и как они влияют на качество и размер модели.
  • Как просмотреть 3D-модель в интернете

Что вам понадобится

2. Настройка

Клонируйте репозиторий Github, используя следующую команду в командной строке:

git clone https://github.com/google/draco

Перейдите в корневой каталог Draco.

cd draco

3. Создайте кодировщик.

Для начала, чтобы разобраться с кодированием и декодированием Draco, давайте сначала разработаем приложения.

Build Encoder

  • Запустите cmake из каталога, где вы хотите сгенерировать файлы сборки, и передайте ему путь к вашему репозиторию Draco.
mkdir build

cd build

cmake ../

make

4. Закодируйте свой первый 3D-объект.

draco_encoder будет считывать на вход файлы OBJ или PLY и выдавать файлы, закодированные в формате Draco. Для тестирования мы включили модель кролика из Стэнфорда. Базовая командная строка выглядит следующим образом:

./draco_encoder -i ../testdata/bun_zipper.ply -o bunny.drc

Теперь вы можете посмотреть на размер выходного файла и сравнить его с размером исходного файла .ply. Сжатый файл должен быть значительно меньше исходного.

Примечание: размер сжатого файла может варьироваться в зависимости от параметров сжатия.

5. Расшифруйте файл Draco в браузере.

На этом этапе мы начнём с простой веб-страницы для расшифровки файлов Draco. Для начала скопируем и вставим следующие фрагменты кода в текстовый редактор.

  1. Начните с базового HTML-файла.
<!DOCTYPE html>
<html>
<head>
  <title>Codelab - Draco Decoder</title>
  1. Следующий фрагмент кода загрузит декодер Draco WASM.
  <script src="https://www.gstatic.com/draco/versioned/decoders/1.4.1/draco_wasm_wrapper.js">
    // It is recommended to always pull your Draco JavaScript and WASM decoders
    // from the above URL. Users will benefit from having the Draco decoder in
    // cache as more sites start using the static URL.
  </script>
  1. Далее добавьте эту функцию, которая создаст модуль декодера Draco. Создание модуля декодера происходит асинхронно, поэтому вам нужно дождаться вызова функции обратного вызова, прежде чем вы сможете использовать модуль.
  <script>
    'use strict';

    // The global Draco decoder module.
    let decoderModule = null;

    // Creates the Draco decoder module.
    function createDracoDecoderModule() {
      let dracoDecoderType = {};

      // Callback when the Draco decoder module is fully instantiated. The
      // module parameter is the created Draco decoder module.
      dracoDecoderType['onModuleLoaded'] = function(module) {
        decoderModule = module;

        // Download the Draco encoded file and decode.
        downloadEncodedMesh('bunny.drc');
      };
      DracoDecoderModule(dracoDecoderType);
    }
  1. Добавьте функцию для декодирования сетки, закодированной в формате Draco.
    // Decode an encoded Draco mesh. byteArray is the encoded mesh as
    // an Uint8Array.
    function decodeMesh(byteArray) {
      // Create the Draco decoder.
      const decoder = new decoderModule.Decoder();

      // Create a buffer to hold the encoded data.
      const buffer = new decoderModule.DecoderBuffer();
      buffer.Init(byteArray, byteArray.length);

      // Decode the encoded geometry.
      let outputGeometry = new decoderModule.Mesh();
      let decodingStatus = decoder.DecodeBufferToMesh(buffer, outputGeometry);

      alert('Num points = ' + outputGeometry.num_points());

      // You must explicitly delete objects created from the DracoModule
      // or Decoder.
      decoderModule.destroy(outputGeometry);
      decoderModule.destroy(decoder);
      decoderModule.destroy(buffer);
    }
  1. Теперь, когда у нас есть функция декодирования Draco, добавим функцию для загрузки закодированной сетки Draco. Функция 'downloadEncodedMesh' принимает в качестве параметра файл Draco для загрузки. В данном случае это будет файл 'bunny.drc' из предыдущего этапа.
    // Download and decode the Draco encoded geometry.
    function downloadEncodedMesh(filename) {
      // Download the encoded file.
      const xhr = new XMLHttpRequest();
      xhr.open("GET", filename, true);
      xhr.responseType = "arraybuffer";
      xhr.onload = function(event) {
        const arrayBuffer = xhr.response;
        if (arrayBuffer) {
          const byteArray = new Uint8Array(arrayBuffer);
          decodeMesh(byteArray);
        }
      };
      xhr.send(null);
    }
  1. Вызовите функцию 'createDracoDecoderModule' для создания модуля декодера Draco, который вызовет функцию 'downloadEncodedMesh' для загрузки закодированного файла Draco, а та, в свою очередь, вызовет функцию 'decodeMesh' для декодирования закодированной сетки Draco.
    // Create the Draco decoder module.
    createDracoDecoderModule();
  </script>
</head>
<body>
</body>
</html>
  1. Сохраните этот файл как "DracoDecode.html"
  2. Запустите веб-сервер на Python. В терминале введите:
python -m SimpleHTTPServer

  1. Откройте в Chrome страницу localhost:8000/DracoDecode.html . Должно отобразиться всплывающее сообщение с указанием количества точек (Num points = 34834), декодированных из модели.

6. Отобразите файл Draco с помощью three.js

Теперь, когда мы знаем, как декодировать файл Draco с помощью WASM, мы воспользуемся популярным веб-просмотрщиком 3D-моделей — three.js. Как и в предыдущем примере, начнем с копирования и вставки следующих фрагментов кода в текстовый редактор.

  1. Начните с базового HTML-файла.
<!DOCTYPE html>
<html>
<head>
  <title>Codelab - Draco three.js Render</title>
  1. Добавьте код для загрузки three.js и загрузчика Draco three.js.
  <script type="importmap">
          {
            "imports": {
              "three": "https://unpkg.com/three@v0.162.0/build/three.module.js",
              "three/addons/": "https://unpkg.com/three@v0.162.0/examples/jsm/"
            }
          }
  </script>
  1. Настройте путь декодера Draco.
  <script type="module">
    // import three.js and DRACOLoader.
    import * as THREE from 'three';
    import {DRACOLoader} from 'three/addons/loaders/DRACOLoader.js'

    // three.js globals.
    var camera, scene, renderer;

    // Create the Draco loader.
    var dracoLoader = new DRACOLoader();

    // Specify path to a folder containing WASM/JS decoding libraries.
    // It is recommended to always pull your Draco JavaScript and WASM decoders
    // from the below URL. Users will benefit from having the Draco decoder in
    // cache as more sites start using the static URL.
    dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.4.1/');
  1. Добавьте код рендеринга в файл three.js.
    function initThreejs() {
      camera = new THREE.PerspectiveCamera( 35, window.innerWidth / window.innerHeight, 0.1, 15 );
      camera.position.set( 3, 0.25, 3 );

      scene = new THREE.Scene();
      scene.background = new THREE.Color( 0x443333 );
      scene.fog = new THREE.Fog( 0x443333, 1, 4 );

      // Ground
      var plane = new THREE.Mesh(
        new THREE.PlaneGeometry( 8, 8 ),
        new THREE.MeshPhongMaterial( { color: 0x999999, specular: 0x101010 } )
      );
      plane.rotation.x = - Math.PI / 2;
      plane.position.y = 0.03;
      plane.receiveShadow = true;
      scene.add(plane);

      // Lights
      var light = new THREE.HemisphereLight( 0x443333, 0x111122 );
      scene.add( light );

      var light = new THREE.SpotLight();
      light.angle = Math.PI / 16;
      light.penumbra = 0.5;
      light.castShadow = true;
      light.position.set( - 1, 1, 1 );
      scene.add( light );

      // renderer
      renderer = new THREE.WebGLRenderer( { antialias: true } );
      renderer.setPixelRatio( window.devicePixelRatio );
      renderer.setSize( window.innerWidth, window.innerHeight );
      renderer.shadowMap.enabled = true;

      const container = document.getElementById('container');
      container.appendChild( renderer.domElement );

      window.addEventListener( 'resize', onWindowResize, false );
    }

    function onWindowResize() {
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();

      renderer.setSize( window.innerWidth, window.innerHeight );
    }

    function animate() {
      render();
      requestAnimationFrame( animate );
    }

    function render() {
      var timer = Date.now() * 0.0003;

      camera.position.x = Math.sin( timer ) * 0.5;
      camera.position.z = Math.cos( timer ) * 0.5;
      camera.lookAt( new THREE.Vector3( 0, 0.1, 0 ) );

      renderer.render( scene, camera );
    }
  1. Добавить код загрузки и декодирования Draco.
    function loadDracoMesh(dracoFile) {
      dracoLoader.load(dracoFile, function ( geometry ) {
        geometry.computeVertexNormals();

        var material = new THREE.MeshStandardMaterial( { vertexColors: THREE.VertexColors } );
        var mesh = new THREE.Mesh( geometry, material );
        mesh.castShadow = true;
        mesh.receiveShadow = true;
        scene.add( mesh );
      } );
    }

  1. Добавьте код для загрузки файла.
    window.onload = function() {
      initThreejs();
      animate();
      loadDracoMesh('bunny.drc');
    }
  </script>
</head>
<body>
  <div id="container"></div>
</body>
</html>
  1. Сохраните этот файл как "DracoRender.html"
  2. При необходимости перезапустите веб-сервер.
python -m SimpleHTTPServer

  1. Откройте файл localhost:8000/DracoRender.html в Chrome. Теперь вы должны увидеть свой файл Draco, отображаемый в браузере.

7. Попробуйте разные параметры кодирования.

Кодировщик Draco позволяет задавать множество различных параметров, влияющих на размер сжатого файла и визуальное качество кода. Попробуйте выполнить следующие несколько команд в командной строке и посмотрите на результаты.

  1. Следующая команда квантует позиции модели, используя 12 (по умолчанию 11) бит.
./draco_encoder -i ../testdata/bun_zipper.ply -o out12.drc -qp 12

  1. Обратите внимание на размер файла out12.drc по сравнению с файлом bunny.drc, упомянутым в предыдущем разделе. Использование большего количества битов квантования может увеличить размер сжатого файла.

3. Следующая команда квантует позиции модели, используя 6 бит.

./draco_encoder -i ../testdata/bun_zipper.ply -o out6.drc -qp 6

  1. Обратите внимание на размер файла out6.drc по сравнению с файлом bunny.drc из предыдущего раздела. Использование меньшего количества битов квантования может уменьшить размер сжатого файла.
  1. Следующие параметры влияют на уровень сжатия модели. Используя флаг cl, вы можете настроить степень сжатия от 1 (минимальный коэффициент сжатия) до 10 (максимальный).
./draco_encoder -i ../testdata/bun_zipper.ply -o outLow.drc -cl 1

./draco_encoder -i ../testdata/bun_zipper.ply -o outHigh.drc -cl 10

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

8. Поздравляем!

Вы успешно завершили лабораторную работу по коду сжатия сетки в Draco и изучили множество ключевых функций Draco!

Надеюсь, вам стало понятно, как Draco может помочь уменьшить размер ваших 3D-моделей и повысить эффективность их передачи через Интернет. Вы можете узнать больше о Draco и получить последнюю версию библиотеки на GitHub .