Como otimizar dados 3D com a compactação de geometria do Draco

1. Visão geral

Os gráficos 3D são uma parte fundamental de muitas aplicações, incluindo jogos, design e visualização de dados. Com o avanço dos processadores gráficos e das ferramentas de criação, modelos 3D maiores e mais complexos se tornarão comuns e ajudarão a alimentar novos aplicativos de realidade virtual imersiva (RV) e realidade aumentada (RA). Devido a essa maior complexidade de modelo, os requisitos de armazenamento e largura de banda são forçados a acompanhar a explosão de dados 3D.

Com o Draco, os aplicativos que usam gráficos 3D podem ser significativamente menores sem comprometer a fidelidade visual. Para os usuários, isso significa que os aplicativos podem ser baixados mais rapidamente, gráficos 3D no navegador podem ser carregados mais rapidamente e as cenas de RV e RA agora podem ser transmitidas com uma fração da largura de banda, renderizadas rapidamente e com aparência fantástica.

O que é o Draco?

O Draco é uma biblioteca para compactar e descompactar malhas geométricas 3D e nuvens de pontos. O objetivo é melhorar o armazenamento e a transmissão de gráficos 3D.

O Draco foi projetado e criado para oferecer eficiência e velocidade de compressão. O código suporta pontos de compactação, informações de conectividade, coordenadas de textura, informações de cor, normais e qualquer outro atributo genérico associado à geometria. O Draco é lançado como um código-fonte C++ que pode ser usado para compactar gráficos 3D, bem como decodificadores C++ e JavaScript para os dados codificados.

O que você vai aprender

  • Como usar o Draco para compactar um modelo 3D
  • Como usar diferentes modelos de compactação e como eles afetam a qualidade e o tamanho do modelo
  • Como visualizar um modelo 3D na Web

O que é necessário

2. Etapas da configuração

Clone o repositório do GitHub usando esta linha de comando:

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

Navegue até o diretório raiz do Draco.

cd draco

3. Construir o codificador

Para começar com codificação e decodificação do Draco, vamos começar criando os aplicativos.

Codificador de build

  • Execute o cmake em um diretório para gerar arquivos de build e transmita o caminho para o repositório do Draco.
mkdir build

cd build

cmake ../

make

4. Codificar seu primeiro recurso 3D

O draco_encoder vai ler arquivos OBJ ou PLY como entrada e gerar arquivos codificados pelo Draco. Incluímos a malha Bunny de Stanford para testes. A linha de comando básica tem esta aparência:

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

Agora você pode ver o tamanho do arquivo de saída e comparar com o arquivo .ply original. O arquivo compactado deve ser muito menor que o tamanho do arquivo original.

Observação: o tamanho compactado pode variar de acordo com as opções de compactação.

5. Decodificar um arquivo do Draco no navegador

Neste ponto, começaremos com uma página da Web básica para decodificar arquivos do Draco. Vamos começar copiando e colando as seguintes seções de código no editor de texto.

  1. Comece com um arquivo HTML básico.
<!DOCTYPE html>
<html>
<head>
  <title>Codelab - Draco Decoder</title>
  1. O snippet de código a seguir vai carregar o decodificador do 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. Em seguida, adicione essa função, que criará um módulo decodificador do Draco. A criação do módulo decodificador é assíncrona, portanto, você precisa esperar até que o callback seja chamado antes de usar o 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. Adicione a função para decodificar uma malha codificada pelo 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. Agora que a função de decodificação do Draco está pronta, adicione uma função para fazer o download de uma malha codificada pelo Draco. A função "downloadEncodedMesh" aceita um parâmetro para que o arquivo do Draco seja carregado. Neste caso, será "bunny.drc". da 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. Chame o método "createDracoDecoderModule" para criar o módulo decodificador do Draco, que chamará "downloadEncodedMesh" para fazer o download do arquivo codificado do Draco, que chamará a função "decodeMesh" para decodificar a malha do Draco codificada.
    // Create the Draco decoder module.
    createDracoDecoderModule();
  </script>
</head>
<body>
</body>
</html>
  1. Salve esse arquivo como "DracoDecode.html"
  2. Inicie o servidor da Web Python. No tipo de terminal:
python -m SimpleHTTPServer

  1. Abra localhost:8000/DracoDecode.html no Chrome. Isso exibirá uma caixa de mensagem de alerta com o número de pontos (Num pontos = 34.834) que foram decodificados do modelo.

6. Renderizar um arquivo do Draco com three.js

Agora que sabemos como decodificar um arquivo do Draco usando o WASM, usaremos um visualizador em 3D conhecido da Web, o three.js. Como no exemplo anterior, vamos começar copiando e colando as seguintes seções de código no editor de texto.

  1. Começar com um arquivo HTML básico
<!DOCTYPE html>
<html>
<head>
  <title>Codelab - Draco three.js Render</title>
  1. Adicione o código para carregar o três.js e o carregador três.js do 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. Configure o caminho do decodificador do 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. Adicionar o código de renderização de três.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. Adicione o código de carregamento e decodificação do 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. Adicione o código para carregar o arquivo.
    window.onload = function() {
      initThreejs();
      animate();
      loadDracoMesh('bunny.drc');
    }
  </script>
</head>
<body>
  <div id="container"></div>
</body>
</html>
  1. Salve o arquivo como "DracoRender.html"
  2. Se necessário, reinicie o servidor da Web.
python -m SimpleHTTPServer

  1. Abra localhost:8000/DracoRender.html no Chrome. Agora o arquivo do Draco deve estar renderizado no navegador.

7. Testar parâmetros de codificação diferentes

O codificador do Draco permite muitos parâmetros diferentes que afetam o tamanho do arquivo compactado e a qualidade visual do código. Tente executar os próximos comandos na linha de comando e conferir os resultados.

  1. O comando a seguir quantiza as posições do modelo usando 12 (o padrão é 11) bits.
./draco_encoder -i ../testdata/bun_zipper.ply -o out12.drc -qp 12

  1. Observe o tamanho de out12.drc em comparação com o arquivo bunny.drc na seção anterior. Usar mais bits de quantização pode aumentar o tamanho do arquivo compactado.

3.O comando a seguir quantiza as posições do modelo usando 6 bits.

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

  1. Observe o tamanho de out6.drc em comparação com o arquivo bunny.drc na seção anterior. Usar menos bits de quantização pode reduzir o tamanho do arquivo compactado.
  1. Os parâmetros a seguir afetam os níveis de compactação do modelo. Usando a flag cl, é possível ajustar a compactação de 1 (menor taxa de compactação) a 10 (mais 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

Observe a saída do codificador do Draco. Perceba que há uma compensação no tempo necessário para compactar nos níveis mais altos de compactação, em comparação com a economia em bits. O parâmetro correto para seu aplicativo dependerá dos requisitos de tempo e tamanho no momento da codificação.

8. Parabéns

Você concluiu o laboratório de código de compactação de malha do Draco e conheceu vários recursos importantes do produto.

Esperamos que esteja claro para você como o Draco pode tornar seus recursos 3D menores e mais eficientes para transmissão pela Web. Saiba mais sobre o Draco e faça o download da biblioteca mais recente no github (link em inglês).