使用 Draco 幾何圖形壓縮改善 3D 資料

使用 Draco 幾何圖形壓縮改善 3D 資料

程式碼研究室簡介

subject上次更新時間:3月 11, 2024
account_circle作者:Google 員工

1. 總覽

3D 圖形是許多應用程式的基本要素,包括遊戲、設計和資料視覺化。隨著圖形處理器和創作工具持續改進,越來越大、複雜的 3D 模型變得越來越普遍,並有助於在沉浸式虛擬實境 (VR) 和擴增實境 (AR) 技術中推動新的應用程式。由於模型複雜度提高,儲存空間和頻寬需求必須跟上 3D 資料的爆炸期保持同步。

採用 Draco 後,使用 3D 圖形的應用程式大小可以在不影響視覺擬真度的情況下,大幅縮小。對使用者來說,應用程式的下載速度越來越快、瀏覽器中的 3D 圖形可加快載入速度,而且現在只要稍微調整頻寬,即可傳輸 VR 和 AR 場景,既快速又賞心悅目。

Draco 程式庫可用來壓縮及解壓縮 3D 幾何圖形網格點雲。目的是改善 3D 圖形的儲存與傳輸效能。

Draco 是專為壓縮效率和速度而設計,程式碼支援壓縮點、連線資訊、紋理座標、色彩資訊、常態以及與幾何圖形相關的任何其他一般屬性。Draco 發布為 C++ 原始碼,可用於壓縮編碼資料的 3D 圖形以及 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 編碼的檔案。我們在測試時納入了史丹佛的兔子網狀網路測試環境。基本指令列如下所示:

./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」函式建立 Draco 解碼器模組,這會呼叫「downloadEncodedMesh」函式來下載經過編碼的 Draco 檔案,這個檔案會呼叫「decodeMesh」函式,解碼已編碼的 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。畫面上應該會顯示快訊訊息方塊,當中包含已從模型解碼的點數 (Num 積分 = 34834)。

6. 使用 Three.js 轉譯 Draco 檔案

瞭解到如何使用 WASM 解碼 Draco 檔案後,接下來我們將使用熱門的網路 3D 檢視器 3.js。如上例所示,我們會先複製以下程式碼區段,並貼到文字編輯器中。

  1. 從基本 HTML 檔案開始
<!DOCTYPE html>
<html>
<head>
 
<title>Codelab - Draco three.js Render</title>
  1. 新增程式碼,載入 third.js 和 Dracothree.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. 新增 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. 新增 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. 請注意,相較於上一節的 bunny.drc 檔案,out12.drc 的大小。使用更多量化位元可能會增加壓縮檔案的大小。

3. 下列指令會使用 6 位元量化模型的位置。

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

  1. 請注意,相較於上一節的 bunny.drc 檔案,out6.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 網狀壓縮程式碼研究室,並成功探索 Draco 的許多主要功能!

希望大家明白,Draco 可以幫你縮小 3D 素材資源,並提升透過網路傳輸效率。如要進一步瞭解 Draco 並取得最新的程式庫,請前往 github