1. Tổng quan
Đồ hoạ 3D là một phần cơ bản của nhiều ứng dụng, bao gồm cả trò chơi, thiết kế và trực quan hoá dữ liệu. Khi bộ xử lý đồ hoạ và các công cụ sáng tạo tiếp tục cải thiện, các mô hình 3D lớn và phức tạp hơn sẽ trở nên phổ biến và giúp thúc đẩy các ứng dụng mới trong công nghệ thực tế ảo (VR) và thực tế tăng cường (AR) sống động. Do độ phức tạp của mô hình tăng lên, các yêu cầu về bộ nhớ và băng thông buộc phải bắt kịp sự bùng nổ của dữ liệu 3D.
Với Draco, các ứng dụng sử dụng đồ hoạ 3D có thể nhỏ hơn đáng kể mà không ảnh hưởng đến độ trung thực về hình ảnh. Đối với người dùng, điều này có nghĩa là giờ đây, họ có thể tải ứng dụng xuống nhanh hơn, đồ hoạ 3D trong trình duyệt có thể tải nhanh hơn và các cảnh thực tế ảo (VR) và thực tế tăng cường (AR) hiện có thể được truyền với một phần băng thông, hiển thị nhanh chóng và trông rất đẹp.
Draco là gì?
Draco là một thư viện để nén và giải nén lưới hình học 3D và đám mây điểm. Mục đích của định dạng này là cải thiện khả năng lưu trữ và truyền tải đồ hoạ 3D.
Draco được thiết kế và xây dựng để tăng hiệu quả và tốc độ nén. Mã này hỗ trợ việc nén các điểm, thông tin kết nối, toạ độ hoạ tiết, thông tin màu sắc, các giá trị chuẩn và mọi thuộc tính chung khác được liên kết với hình học. Draco được phát hành dưới dạng mã nguồn C++ có thể dùng để nén đồ hoạ 3D cũng như các trình giải mã C++ và Javascript cho dữ liệu được mã hoá.
Kiến thức bạn sẽ học được
- Cách sử dụng Draco để nén mô hình 3D
- Cách sử dụng các mô hình nén khác nhau và mức độ ảnh hưởng của các mô hình này đến chất lượng và kích thước của mô hình
- Cách xem mô hình 3D trên web
Bạn cần có
2. Thiết lập
Sao chép kho lưu trữ Github bằng dòng lệnh sau:
git clone https://github.com/google/draco
Chuyển đến thư mục gốc Draco.
cd draco
3. Tạo bộ mã hoá
Để bắt đầu với quy trình mã hoá và giải mã Draco, trước tiên, hãy bắt đầu bằng cách tạo các ứng dụng.
Bộ mã hoá bản dựng
- Chạy cmake từ một thư mục mà bạn muốn tạo tệp bản dựng và truyền đường dẫn đến kho lưu trữ Draco.
mkdir build
cd build
cmake ../
make
4. Mã hoá thành phần 3D đầu tiên
draco_encoder sẽ đọc các tệp OBJ hoặc PLY làm dữ liệu đầu vào và xuất ra các tệp được mã hoá bằng Draco. Chúng tôi đã đưa mô hình Bunny của Stanford vào để kiểm thử. Dòng lệnh cơ bản sẽ có dạng như sau:
./draco_encoder -i ../testdata/bun_zipper.ply -o bunny.drc
Giờ đây, bạn có thể xem kích thước của tệp đầu ra và so sánh với tệp .ply gốc. Tệp nén phải có kích thước nhỏ hơn nhiều so với kích thước tệp gốc.
Lưu ý: Kích thước nén có thể thay đổi tuỳ theo các lựa chọn nén.
5. Giải mã tệp Draco trong trình duyệt
Tại thời điểm này, chúng ta sẽ bắt đầu bằng một trang web cơ bản để giải mã các tệp Draco. Chúng ta sẽ bắt đầu bằng cách sao chép và dán các phần mã sau vào trình chỉnh sửa văn bản.
- Bắt đầu bằng tệp HTML cơ bản.
<!DOCTYPE html>
<html>
<head>
<title>Codelab - Draco Decoder</title>
- Đoạn mã sau đây sẽ tải trình giải mã 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>
- Tiếp theo, hãy thêm hàm này để tạo một mô-đun giải mã Draco. Việc tạo mô-đun bộ giải mã là không đồng bộ, vì vậy, bạn cần đợi cho đến khi lệnh gọi lại được gọi thì mới có thể sử dụng mô-đun.
<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);
}
- Thêm hàm để giải mã một lưới được mã hoá bằng 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);
}
- Bây giờ, khi đã có hàm giải mã Draco, hãy thêm một hàm để tải một lưới được mã hoá bằng Draco xuống. Hàm "downloadEncodedMesh" chấp nhận một tham số cho tệp Draco cần tải. Trong trường hợp này, đó sẽ là "bunny.drc" từ giai đoạn trước.
// 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);
}
- Gọi hàm "createDracoDecoderModule" để tạo mô-đun giải mã Draco. Mô-đun này sẽ gọi hàm "downloadEncodedMesh" để tải tệp Draco đã mã hoá xuống. Tệp này sẽ gọi hàm "decodeMesh" để giải mã lưới Draco đã mã hoá.
// Create the Draco decoder module.
createDracoDecoderModule();
</script>
</head>
<body>
</body>
</html>
- Lưu tệp này dưới dạng "DracoDecode.html"
- Khởi động máy chủ web Python. Trong ứng dụng Terminal, hãy nhập:
python -m SimpleHTTPServer
- Mở localhost:8000/DracoDecode.html trong Chrome. Thao tác này sẽ hiển thị một hộp thông báo cảnh báo có số điểm (Num points = 34834) đã được giải mã từ mô hình.
6. Kết xuất tệp Draco bằng three.js
Giờ đây, khi đã biết cách giải mã tệp Draco bằng WASM, chúng ta sẽ sử dụng một trình xem 3D phổ biến trên web – three.js. Như trong ví dụ trước, chúng ta sẽ bắt đầu bằng cách sao chép và dán các phần mã sau vào trình chỉnh sửa văn bản.
- Bắt đầu bằng tệp HTML cơ bản
<!DOCTYPE html>
<html>
<head>
<title>Codelab - Draco three.js Render</title>
- Thêm mã để tải three.js và trình tải 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>
- Thiết lập đường dẫn bộ giải mã 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/');
- Thêm mã kết xuất 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 );
}
- Thêm mã tải và giải mã 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 );
} );
}
- Thêm mã để tải tệp.
window.onload = function() {
initThreejs();
animate();
loadDracoMesh('bunny.drc');
}
</script>
</head>
<body>
<div id="container"></div>
</body>
</html>
- Lưu tệp này dưới dạng "DracoRender.html"
- Nếu cần, hãy khởi động lại máy chủ web.
python -m SimpleHTTPServer
- Mở localhost:8000/DracoRender.html trong Chrome. Giờ đây, bạn sẽ thấy tệp Draco được kết xuất trong trình duyệt.
7. Thử các thông số mã hoá khác nhau
Trình mã hoá Draco cho phép nhiều tham số khác nhau ảnh hưởng đến kích thước của tệp nén và chất lượng hình ảnh của mã. Hãy thử chạy một vài lệnh tiếp theo trong dòng lệnh và xem kết quả.
- Lệnh sau đây lượng tử hoá các vị trí của mô hình bằng cách sử dụng 12 bit (mặc định là 11).
./draco_encoder -i ../testdata/bun_zipper.ply -o out12.drc -qp 12
- Lưu ý kích thước của out12.drc so với tệp bunny.drc trong phần trước. Việc sử dụng nhiều bit lượng tử hoá hơn có thể làm tăng kích thước của tệp nén.
3.Lệnh sau đây sẽ lượng tử hoá các vị trí của mô hình bằng 6 bit.
./draco_encoder -i ../testdata/bun_zipper.ply -o out6.drc -qp 6
- Lưu ý kích thước của out6.drc so với tệp bunny.drc trong phần trước. Việc sử dụng ít bit lượng tử hoá hơn có thể giảm kích thước của tệp nén.
- Các tham số sau đây ảnh hưởng đến mức độ nén của mô hình. Khi sử dụng cờ cl, bạn có thể điều chỉnh mức nén từ 1 (tỷ lệ nén thấp nhất) đến 10 (tỷ lệ nén cao nhất).
./draco_encoder -i ../testdata/bun_zipper.ply -o outLow.drc -cl 1
./draco_encoder -i ../testdata/bun_zipper.ply -o outHigh.drc -cl 10
Lưu ý đầu ra từ bộ mã hoá Draco. Bạn có thể thấy rằng có sự đánh đổi về thời gian cần thiết để nén ở mức nén cao nhất so với mức tiết kiệm về số bit. Tham số phù hợp cho ứng dụng của bạn sẽ phụ thuộc vào yêu cầu về thời gian và kích thước tại thời điểm mã hoá.
8. Xin chúc mừng
Bạn đã hoàn thành lớp học lập trình về tính năng nén lưới Draco và khám phá thành công nhiều tính năng chính của Draco!
Hy vọng bạn đã hiểu rõ cách Draco có thể giúp giảm kích thước và tăng hiệu quả truyền tải tài sản 3D trên web. Bạn có thể tìm hiểu thêm về Draco và tải thư viện mới nhất xuống từ github.