Optymalizacja danych 3D przy użyciu narzędzia Draco Geometry Compression

1. Przegląd

Grafika 3D jest podstawowym elementem wielu aplikacji, w tym gier, programów do projektowania i wizualizacji danych. W miarę jak procesory graficzne i narzędzia do tworzenia będą się rozwijać, większe i bardziej złożone modele 3D staną się powszechne i będą napędzać nowe zastosowania w rzeczywistości wirtualnej (VR) i rzeczywistości rozszerzonej (AR). Ze względu na rosnącą złożoność modelu wymagania dotyczące pamięci i przepustowości muszą nadążać za eksplozją danych 3D.

Dzięki Draco aplikacje korzystające z grafiki 3D mogą być znacznie mniejsze bez utraty jakości obrazu. Dla użytkowników oznacza to, że aplikacje można teraz pobierać szybciej, grafika 3D w przeglądarce może się szybciej wczytywać, a sceny VR i AR mogą być przesyłane przy użyciu ułamka przepustowości, szybko renderowane i wyglądać fantastycznie.

Co to jest Draco?

Draco to biblioteka do kompresowania i dekompresowania trójwymiarowych siatek geometrycznych i chmur punktów. Ma on na celu usprawnienie przechowywania i przesyłania grafiki 3D.

Draco został zaprojektowany i stworzony z myślą o wydajności i szybkości kompresji. Kod obsługuje kompresję punktów, informacji o łączności, współrzędnych tekstury, informacji o kolorze, wektorów normalnych i wszelkich innych atrybutów ogólnych powiązanych z geometrią. Draco jest udostępniany jako kod źródłowy C++, który można wykorzystać do kompresji grafiki 3D, a także jako dekodery C++ i JavaScript do zakodowanych danych.

Czego się nauczysz

  • Jak używać Draco do kompresowania modelu 3D
  • Jak korzystać z różnych modeli kompresji i jak wpływają one na jakość i rozmiar modelu
  • Jak wyświetlić model 3D w przeglądarce

Czego potrzebujesz

2. Przygotowania

Sklonuj repozytorium GitHub za pomocą tego wiersza poleceń:

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

Przejdź do katalogu głównego Draco.

cd draco

3. Tworzenie kodera

Aby rozpocząć kodowanie i dekodowanie Draco, zacznijmy od utworzenia aplikacji.

Tworzenie kodera

  • Uruchom cmake w katalogu, w którym chcesz wygenerować pliki kompilacji, i przekaż mu ścieżkę do repozytorium Draco.
mkdir build

cd build

cmake ../

make

4. Kodowanie pierwszego zasobu 3D

Program draco_encoder odczytuje pliki OBJ lub PLY jako dane wejściowe i zapisuje pliki zakodowane w formacie Draco. Do testowania dołączyliśmy siatkę królika Stanforda. Podstawowy wiersz poleceń wygląda tak:

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

Możesz teraz sprawdzić rozmiar pliku wyjściowego i porównać go z rozmiarem oryginalnego pliku .ply. Skompresowany plik powinien być znacznie mniejszy niż oryginalny.

Uwaga: rozmiar skompresowanego pliku może się różnić w zależności od opcji kompresji.

5. Dekodowanie pliku Draco w przeglądarce

Zaczniemy od podstawowej strony internetowej do dekodowania plików Draco. Zacznij od skopiowania i wklejenia do edytora tekstu tych sekcji kodu:

  1. Zacznij od podstawowego pliku HTML.
<!DOCTYPE html>
<html>
<head>
  <title>Codelab - Draco Decoder</title>
  1. Poniższy fragment kodu wczyta dekoder 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. Następnie dodaj tę funkcję, która utworzy moduł dekodera Draco. Tworzenie modułu dekodera jest asynchroniczne, więc zanim zaczniesz go używać, musisz poczekać na wywołanie zwrotne.
  <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. Dodaj funkcję dekodowania siatki zakodowanej w formacie 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. Po dodaniu funkcji dekodowania Draco dodaj funkcję pobierania siatki zakodowanej w Draco. Funkcja „downloadEncodedMesh” akceptuje parametr pliku Draco, który ma zostać wczytany. W tym przypadku będzie to „bunny.drc” z poprzedniego etapu.
    // 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. Wywołaj funkcję „createDracoDecoderModule”, aby utworzyć moduł dekodera Draco, który wywoła funkcję „downloadEncodedMesh” w celu pobrania zakodowanego pliku Draco, a następnie wywoła funkcję „decodeMesh” w celu zdekodowania zakodowanej siatki Draco.
    // Create the Draco decoder module.
    createDracoDecoderModule();
  </script>
</head>
<body>
</body>
</html>
  1. Zapisz ten plik jako „DracoDecode.html”.
  2. Uruchom serwer WWW w Pythonie. W terminalu wpisz:
python -m SimpleHTTPServer

  1. Otwórz localhost:8000/DracoDecode.html w Chrome. Powinno się wyświetlić okno z alertem zawierające liczbę punktów (Num points = 34834) zdekodowanych z modelu.

6. Renderowanie pliku Draco za pomocą three.js

Wiemy już, jak dekodować plik Draco za pomocą WASM. Teraz użyjemy popularnej przeglądarki 3D w internecie – three.js. Podobnie jak w poprzednim przykładzie zaczniemy od skopiowania i wklejenia do edytora tekstu tych sekcji kodu:

  1. Zacznij od podstawowego pliku HTML
<!DOCTYPE html>
<html>
<head>
  <title>Codelab - Draco three.js Render</title>
  1. Dodaj kod, aby wczytać three.js i ładowarkę 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. Skonfiguruj ścieżkę dekodera 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. Dodaj kod renderowania 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. Dodaj kod wczytywania i dekodowania 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. Dodaj kod, aby wczytać plik.
    window.onload = function() {
      initThreejs();
      animate();
      loadDracoMesh('bunny.drc');
    }
  </script>
</head>
<body>
  <div id="container"></div>
</body>
</html>
  1. Zapisz ten plik jako „DracoRender.html”.
  2. W razie potrzeby uruchom ponownie serwer internetowy.
python -m SimpleHTTPServer

  1. Otwórz w Chrome stronę localhost:8000/DracoRender.html. W przeglądarce powinien pojawić się plik Draco.

7. Wypróbuj różne parametry kodowania

Koder Draco umożliwia ustawienie wielu różnych parametrów, które wpływają na rozmiar skompresowanego pliku i jakość wizualną kodu. Uruchom kilka następnych poleceń w wierszu poleceń i sprawdź wyniki.

  1. To polecenie kwantyzuje pozycje modelu przy użyciu 12 bitów (domyślnie jest to 11 bitów).
./draco_encoder -i ../testdata/bun_zipper.ply -o out12.drc -qp 12

  1. Zwróć uwagę na rozmiar pliku out12.drc w porównaniu z rozmiarem pliku bunny.drc z poprzedniej sekcji. Użycie większej liczby bitów kwantyzacji może zwiększyć rozmiar skompresowanego pliku.

3.To polecenie kwantyzuje pozycje modelu przy użyciu 6 bitów.

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

  1. Zwróć uwagę na rozmiar pliku out6.drc w porównaniu z rozmiarem pliku bunny.drc w poprzedniej sekcji. Użycie mniejszej liczby bitów kwantyzacji może zmniejszyć rozmiar skompresowanego pliku.
  1. Na poziomy kompresji modelu wpływają te parametry: Za pomocą flagi cl możesz dostosować kompresję w zakresie od 1 (najniższy współczynnik kompresji) do 10 (najwyższy).
./draco_encoder -i ../testdata/bun_zipper.ply -o outLow.drc -cl 1

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

Zwróć uwagę na dane wyjściowe z enkodera Draco. Widać, że w przypadku najwyższych poziomów kompresji czas potrzebny na skompresowanie jest dłuższy niż oszczędność bitów. Prawidłowy parametr dla Twojej aplikacji będzie zależeć od wymagań dotyczących czasu i rozmiaru w momencie kodowania.

8. Gratulacje

Ukończono laboratorium kodu kompresji siatki Draco i pomyślnie zbadano wiele kluczowych funkcji Draco!

Mamy nadzieję, że teraz wiesz już, jak Draco może pomóc w zmniejszeniu rozmiaru zasobów 3D i zwiększeniu efektywności ich przesyłania przez internet. Więcej informacji o Draco i najnowszą bibliotekę znajdziesz na GitHubie.