Draco Geometry Compression による 3D データの最適化

1. 概要

3D グラフィックスは、ゲーム、デザイン、データの可視化など、多くのアプリケーションの基礎となっています。グラフィック プロセッサや作成ツールが進化するにつれ、より大規模で複雑な 3D モデルが一般的になり、没入型バーチャル リアリティ(VR)や拡張現実(AR)の新しい用途に利用されるようになるでしょう。モデルの複雑さが増したため、ストレージと帯域幅の要件が 3D データの爆発的な増加に対応することを余儀なくされます。

Draco では、視覚的な忠実度を損なうことなく、3D グラフィックスを使用するアプリケーションを大幅に小さくすることができます。ユーザーにとっては、アプリのダウンロードが高速化され、ブラウザでの 3D グラフィックスの読み込みが速くなり、VR や AR シーンをごくわずかな帯域幅で転送して、すばやくレンダリングできるようになり、見栄えも良くなりました。

Draco とは

Draco は、3D ジオメトリ メッシュ点群を圧縮および解凍するためのライブラリです。3D グラフィックの保存と転送を改善することを目的としています。

Draco は圧縮効率と速度を重視して設計、構築されています。このコードは、圧縮ポイント、接続情報、テクスチャ座標、色情報、法線、ジオメトリに関連するその他の一般的な属性をサポートします。Draco は、3D グラフィックスの圧縮に使用できる 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 でエンコードされたファイルを出力します。テストのために、スタンフォード大学の Bunny メッシュも組み込んでいます。基本的なコマンドラインは次のようになります。

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

出力ファイルのサイズを確認して、元の .ply ファイルと比較できます。圧縮ファイルは、元のファイルサイズよりはるかに小さくする必要があります。

注: 圧縮サイズは、圧縮オプションによって異なります。

5. ブラウザで Draco ファイルをデコードする

この時点で、Draco ファイルをデコードするための基本的な Web ページから始めます。まず、次のコード セクションをコピーしてテキスト エディタに貼り付けます。

  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 デコーダ モジュールを作成します。このモジュールは、エンコードされた Draco ファイルをダウンロードします関数を使用して、エンコードされた 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 を開きます。これにより、モデルからデコードされたポイント数(ポイント数 = 34834)を示すアラート メッセージ ボックスが表示されます。

6. Two.js を使用して Draco ファイルをレンダリングする

WASM を使用して Draco ファイルをデコードする方法がわかったところで、今度は一般的なウェブ 3D ビューアである {/5}3D ビューアを使用します。前の例と同様に、まず次のコード セクションをコピーしてテキスト エディタに貼り付けます。

  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. 3.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. Chrome で localhost:8000/DracoRender.html を開きます。ブラウザに 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 メッシュ圧縮の Codelab を終了し、Draco の主な機能の多くを確認しました。

Draco が 3D アセットを小さくし、ウェブ経由で送信する際の効率向上にどのように役立つかがおわかりいただけたでしょうか。Draco の詳細を確認し、github から最新のライブラリを入手できます。