Optimización de datos 3D con la compresión de geometría Draco

1. Descripción general

Los gráficos 3D son una parte fundamental de muchas aplicaciones, entre las que se incluyen los videojuegos, el diseño y la visualización de datos. A medida que los procesadores gráficos y las herramientas de creación continúan mejorando, los modelos 3D más grandes y complejos se volverán comunes y ayudarán a alimentar nuevas aplicaciones de realidad virtual (RV) inmersiva y realidad aumentada (RA). Debido a la mayor complejidad del modelo, los requisitos de almacenamiento y ancho de banda se ven obligados a seguir el ritmo de la explosión de los datos 3D.

Con Draco, las aplicaciones que utilizan gráficos 3D pueden ser bastante más pequeñas sin comprometer la fidelidad visual. Para los usuarios, esto significa que ahora las apps se pueden descargar más rápido, los gráficos 3D del navegador pueden cargarse más rápido y las escenas de RV y RA ahora se pueden transmitir con una fracción del ancho de banda, se renderizan rápidamente y tienen un aspecto fantástico.

¿Qué es Draco?

Draco es una biblioteca para comprimir y descomprimir mallas y nubes de puntos geométricas 3D. Su objetivo es mejorar el almacenamiento y la transmisión de gráficos 3D.

Draco se diseñó y construyó para lograr una velocidad y una eficiencia de compresión eficaces. El código permite comprimir puntos, información de conectividad, coordenadas de textura, información de color, normales y cualquier otro atributo genérico asociado con la geometría. Se lanza Draco como código fuente C++ que se puede usar para comprimir gráficos 3D, además de decodificadores C++ y JavaScript para los datos codificados.

Qué aprenderás

  • Cómo usar Draco para comprimir un modelo 3D
  • Cómo usar diferentes modelos de compresión y cómo afectan su calidad y tamaño
  • Cómo ver un modelo 3D en la Web

Requisitos

2. Cómo prepararte

Clona el repositorio de GitHub con esta línea de comandos:

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

Navega al directorio raíz de Draco.

cd draco

3. Compila el codificador

Para comenzar con la codificación y decodificación de Draco, comenzaremos por compilar las apps.

Codificador de compilaciones

  • Ejecuta cmake desde un directorio en el que quieras generar archivos de compilación y pásale la ruta de acceso a tu repositorio de Draco.
mkdir build

cd build

cmake ../

make

4. Codifica tu primer recurso 3D

draco_encoder leerá archivos OBJ o PLY como entrada y generará archivos codificados en Draco. Incluimos la malla de Bunny de Stanford para las pruebas. La línea de comandos básica se ve de la siguiente manera:

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

Ahora puedes ver el tamaño del archivo de salida y compararlo con el archivo .ply original. El archivo comprimido debe ser mucho más pequeño que su tamaño original.

Nota: El tamaño comprimido puede variar según las opciones de compresión.

5. Cómo decodificar un archivo de Draco en el navegador

En este punto, comenzaremos con una página web básica para decodificar archivos de Draco. Para comenzar, copiaremos y pegaremos las siguientes secciones de código en el editor de texto.

  1. Comienza con el archivo HTML básico.
<!DOCTYPE html>
<html>
<head>
  <title>Codelab - Draco Decoder</title>
  1. Con el siguiente fragmento de código, se cargará el decodificador 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. A continuación, agrega esta función, que creará un módulo de codificador de Draco. La creación del módulo de codificador es asíncrona, por lo que debes esperar hasta que se llame a la devolución de llamada para poder usar el módulo.
  <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. Agrega la función para decodificar una malla codificada en 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. Ahora que ya implementamos la función de decodificación de Draco, agrega una función para descargar una malla codificada en Draco. La función "downloadEncodedMesh" acepta un parámetro para el archivo de Draco que se cargará. En este caso, será “bunny.drc”. de la etapa anterior.
    // 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. Asígnale el nombre “createDracoDecoderModule”. para crear el módulo de decodificador de Draco, que llamará a “downloadEncodedMesh”. para descargar el archivo codificado de Draco, que llamará al método "decodeMesh" para decodificar la malla de Draco codificada.
    // Create the Draco decoder module.
    createDracoDecoderModule();
  </script>
</head>
<body>
</body>
</html>
  1. Guarda este archivo como "DracoDecode.html".
  2. Inicia el servidor web de Python. En el tipo de terminal, haz lo siguiente:
python -m SimpleHTTPServer

  1. Abre localhost:8000/DracoDecode.html en Chrome. Se debería mostrar un cuadro de mensaje de alerta con la cantidad de puntos (Num puntos = 34,834) que se decodificaron del modelo.

6. Renderiza un archivo de Draco con tres.js

Ahora que sabemos cómo decodificar un archivo Draco con WASM, usaremos un popular visor 3D para la Web: tres.js. Al igual que en el ejemplo anterior, comenzaremos por copiar y pegar las siguientes secciones de código en el editor de texto.

  1. Comenzar con un archivo HTML básico
<!DOCTYPE html>
<html>
<head>
  <title>Codelab - Draco three.js Render</title>
  1. Agrega código para cargartres.js y el cargador de tres.js de 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. Configura la ruta de acceso del decodificador de 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. Agrega código de renderización de tres.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. Se agregó el código de carga y decodificación de 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. Agrega código para cargar el archivo.
    window.onload = function() {
      initThreejs();
      animate();
      loadDracoMesh('bunny.drc');
    }
  </script>
</head>
<body>
  <div id="container"></div>
</body>
</html>
  1. Guarda este archivo como "DracoRender.html".
  2. Si es necesario, reinicia el servidor web.
python -m SimpleHTTPServer

  1. Abre localhost:8000/DracoRender.html en Chrome. Ahora deberías ver el archivo Draco renderizado en el navegador.

7. Prueba diferentes parámetros de codificación

El codificador Draco admite muchos parámetros diferentes que afectan el tamaño del archivo comprimido y la calidad visual del código. Intenta ejecutar los siguientes comandos en la línea de comandos y observa los resultados.

  1. El siguiente comando cuantifica las posiciones del modelo con 12 bits (el valor predeterminado es 11).
./draco_encoder -i ../testdata/bun_zipper.ply -o out12.drc -qp 12

  1. Ten en cuenta el tamaño de out12.drc en comparación con el archivo bunny.drc de la sección anterior. El uso de más bits de cuantización puede aumentar el tamaño del archivo comprimido.

3.El siguiente comando cuantifica las posiciones del modelo con 6 bits.

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

  1. Observa el tamaño de out6.drc en comparación con el archivo bunny.drc de la sección anterior. Usar menos bits de cuantización puede reducir el tamaño del archivo comprimido.
  1. Los siguientes parámetros afectan los niveles de compresión del modelo. Con la marca cl, puedes ajustar la compresión de 1 (la proporción de compresión más baja) a 10 (la más alta).
./draco_encoder -i ../testdata/bun_zipper.ply -o outLow.drc -cl 1

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

Toma nota del resultado del codificador de Draco. Puedes ver que existe una compensación en el tiempo necesario para comprimir en los niveles de compresión más altos en comparación con el ahorro en bits. El parámetro correcto para tu aplicación dependerá de los requisitos de tiempo y tamaño en el momento de la codificación.

8. Felicitaciones

Completaste el codelab de código de compresión de malla de Draco y exploraste con éxito muchas funciones clave de Draco.

Esperamos que te quede claro cómo Draco puede ayudar a que tus recursos en 3D sean más pequeños y eficientes a la hora de transmitirlos a través de la web. Puedes obtener más información sobre Draco y la biblioteca más reciente de github.