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

1. Обзор

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

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

Что такое Драко?

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

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

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

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

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

2. Приступаем к настройке

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

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

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

cd draco

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

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

Сборка кодировщика

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

cd build

cmake ../

make

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

draco_encoder будет читать файлы OBJ или PLY в качестве входных данных и выводить файлы в кодировке Draco. Мы включили для тестирования сетку Stanford's Bunny. Основная командная строка выглядит так:

./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. Откройте localhost:8000/DracoDecode.html в Chrome. Должно появиться окно с предупреждением о количестве точек (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 и загрузчика Three.js Draco.
  <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. Добавьте загрузку Драко и декодируйте код.
    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 .