אופטימיזציה של נתוני תלת-ממד באמצעות דחיסה של נתוני גיאומטריה של Draco

1. סקירה כללית

גרפיקה תלת-ממדית היא חלק בסיסי באפליקציות רבות, כולל משחקים, עיצוב והדמיה של נתונים. ככל שמעבדי הגרפיקה וכלי היצירה ממשיכים להשתפר, מודלים תלת-ממדיים גדולים ומורכבים יותר יהפכו להיות נפוצים ויעזרו לעודד אפליקציות חדשות ליהנות ממציאות מדומה (VR) וממציאות רבודה (AR). בשל מורכבות המודל המוגברת, דרישות האחסון ורוחב הפס חייבות לעמוד בקצב של פיצוץ הנתונים התלת-ממדיים.

עם Draco, אפשר להקטין באופן משמעותי אפליקציות שמשתמשות בגרפיקה תלת-ממדית בלי לפגוע באיכות הוויזואלית. מבחינת המשתמשים, המשמעות היא שעכשיו אפשר להוריד אפליקציות מהר יותר, גרפיקה תלת-ממדית בדפדפן יכולה להיטען מהר יותר, וסצנות של VR ו-AR יכולות להישלח עם חלק מרוחב הפס, לעיבוד מהיר יותר ולנראות מעולה.

מה דרקו?

Draco הוא ספרייה לדחיסה ולביטול דחיסה של רשתות גיאומטריות בתלת-ממד ועננים נקודתיים. הוא מיועד לשפר את האחסון וההעברה של גרפיקה תלת-ממדית.

דרקו תוכנן ונבנה כדי לספק יעילות דחיסה ומהירות. הקוד תומך בדחיסת נקודות, פרטי קישוריות, קואורדינטות של טקסטורה, פרטי צבע, נורמליות וכל מאפיין גנרי אחר המשויך לגיאומטריה. Draco מופץ כקוד מקור C++ שיכול לשמש לדחיסת גרפיקה בתלת-ממד, וכן למפענחי C++ ו-JavaScript עבור הנתונים המקודדים.

מה תלמדו

  • איך להשתמש ב-Draco כדי לדחוס מודל תלת-ממדי
  • איך להשתמש במודלים שונים של דחיסה ואיך הם משפיעים על האיכות והגודל של המודל
  • איך להציג מודל תלת-ממדי באינטרנט

למה תזדקק?

2. בתהליך ההגדרה

משכפלים את מאגר ה-GitHub באמצעות שורת הפקודה הבאה:

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

עוברים לספריית הבסיס של Draco.

cd draco

3. יוצרים את המקודד

כדי להתחיל עם קידוד ופענוח של Draco, נתחיל בבניית האפליקציות.

בניית מקודד

  • מריצים את cmake מספרייה שבה רוצים ליצור קובצי build ומעבירים אליו את הנתיב למאגר Draco.
mkdir build

cd build

cmake ../

make

4. קידוד הנכס התלת-ממדי הראשון

draco_encoder יקרא קובצי OBJ או PLY כקלט, ויפיק פלט של קבצים בקידוד Draco. כללנו את רשת ה-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' כדי ליצור את מודול המפענח של דרקו, שייקרא 'downloadEncodedMesh' כדי להוריד את הקובץ המקודד דרקו, שייקרא 'decodeMesh'. כדי לפענח את רשת דרקו המקודדת.
    // 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. תוצג תיבת התראה עם מספר הנקודות (מספר נקודות = 34834) שפוענח מהמודל.

6. עיבוד קובץ של Draco עם שלושה.js

עכשיו, אחרי שאנחנו יודעים איך לפענח קובץ Draco באמצעות WASM, נשתמש במציג התלת-ממד הפופולרי באינטרנט – שלושה.js. כמו בדוגמה הקודמת, נתחיל בהעתקה והדבקה של קטעי הקוד הבאים בכלי לעריכת טקסט.

  1. מתחילים עם קובץ HTML בסיסי
<!DOCTYPE html>
<html>
<head>
  <title>Codelab - Draco three.js Render</title>
  1. הוסף קוד כדי לטעון שלושה.js ו-Draco three.js Loader.
  <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. להוסיף את קוד הרינדור שלושה.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. פותחים את הדף 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. מזל טוב

סיימת את שיעור ה-Lab של דחיסת קוד הרשת של Draco וגילית בהצלחה הרבה תכונות מרכזיות של דרקו.

אני מקווה שברור לכם איך דרקו יכול לעזור להפוך את נכסי התלת-ממד שלכם לקטנים יותר ויעילים יותר לשידור באינטרנט. אפשר לקבל מידע נוסף על דרקו ולקבל את הספרייה העדכנית מ-github.