Three.js + USDC di Browser
Jadi, Anda memiliki file .usdc dari Maya atau Houdini dan ingin menampilkannya dalam scene Three.js. Saya pernah mengalaminya. Sampai baru-baru ini, pilihan yang tersedia kurang ideal. Anda bisa mengonversinya ke glTF secara offline, menyiapkan build WASM dari OpenUSD, atau menyerah dan menggunakan format lain.
Panduan ini menunjukkan cara mengurai USDC secara langsung di JavaScript menggunakan @cinevva/usdjs, serta cara memasukkan data tersebut ke Three.js.
Apa Itu USDC?
File USD hadir dalam tiga jenis. USDA adalah versi teks yang mudah dibaca manusia dan mudah di-debug, tetapi panjang. USDC (terkadang disebut "Crate") adalah format biner yang benar-benar digunakan oleh pipeline produksi karena ringkas dan cepat dimuat dalam alat native. USDZ adalah arsip zip yang berisi USDC beserta tekstur, yang digunakan Apple untuk AR Quick Look.
Masalahnya: USDC adalah format biner proprieter. Pixar menulis implementasi referensinya dalam C++, dan hingga kini belum ada cara berbasis JavaScript murni untuk membacanya. Hal itu mulai berubah: pada Desember 2025, Alliance for OpenUSD menerbitkan Core Specification 1.0, standar pertama yang diratifikasi untuk mendokumentasikan cara data scene OpenUSD disusun, dikomposisikan, dan dipertukarkan, sementara revisi 1.1 sudah dalam proses pengerjaan. Namun, implementasi referensinya masih berada dalam basis kode C++ milik Pixar, sehingga pembaca terpisah tetap menjadi cara praktis untuk mengakses format tersebut dari JavaScript.
Mengapa Ini Mungkin Penting bagi Anda
Jika Anda membuat aplikasi web 3D yang perlu bekerja dengan konten dari pipeline film atau VFX, Anda akan berhadapan dengan USD. Seniman mengekspor dari Maya, Houdini, atau Blender, dan hasil ekspor tersebut sering kali berupa USDC.
Sebelum @cinevva/usdjs, Anda memiliki tiga pilihan. Anda bisa menjalankan usdcat untuk mengonversi USDC ke format teks, yang menambahkan satu langkah build dan menghilangkan keringkasan format binernya. Anda bisa mengompilasi OpenUSD atau TinyUSDZ ke WebAssembly, yang menambahkan beberapa megabita ke bundle dan memerlukan header server khusus untuk threading. Atau Anda bisa menggunakan USDLoader bawaan Three.js, yang menangani USDZ tetapi memiliki dukungan USDC terbatas dan komposisi minimal.
Sekarang ada pilihan keempat: mengurai USDC secara native di JavaScript.
Cara Kerjanya
Library @cinevva/usdjs mengimplementasikan ulang fungsionalitas inti USD dalam TypeScript. Saya membuat parser USDC dengan membaca kode sumber C++ milik Pixar dan menerjemahkan spesifikasi format Crate ke JavaScript. Jika terdapat ambiguitas dalam perilakunya, kami mengikuti cara kerja OpenUSD.
Dalam praktiknya, penggunaannya terlihat seperti ini:
import { parseUsdcToLayer } from '@cinevva/usdjs';
// Fetch the USDC file
const response = await fetch('/model.usdc');
const buffer = await response.arrayBuffer();
// Parse it
const layer = parseUsdcToLayer(buffer, { identifier: 'model.usdc' });
// Now you have a structured layer with prims, properties, and metadata
console.log(layer.pseudoRoot.children);Tanpa WASM. Tanpa kode native. Hanya JavaScript yang membaca byte dan membangun representasi terstruktur.
Menghubungkannya ke Three.js
Mengurai USD hanyalah separuh dari persoalan. Anda juga perlu mengonversi konsep USD (prim, transformasi, dan skema mesh) menjadi objek Three.js.
Berikut contoh minimal yang memuat file USDC dan membuat mesh Three.js:
import * as THREE from 'three';
import { UsdStage, parseUsdcToLayer } from '@cinevva/usdjs';
async function loadUsdcToThree(url: string, scene: THREE.Scene) {
// 1. Fetch and parse
const buffer = await fetch(url).then(r => r.arrayBuffer());
const layer = parseUsdcToLayer(buffer, { identifier: url });
// 2. Create a stage (handles composition if there are references)
const stage = UsdStage.open(layer);
// 3. Walk the prim tree
for (const prim of stage.traverse()) {
// Skip non-geometry
if (prim.typeName !== 'Mesh') continue;
// Get geometry data
const points = prim.getAttribute('points')?.value;
const faceVertexIndices = prim.getAttribute('faceVertexIndices')?.value;
const faceVertexCounts = prim.getAttribute('faceVertexCounts')?.value;
if (!points || !faceVertexIndices || !faceVertexCounts) continue;
// Convert to Three.js geometry
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position',
new THREE.Float32BufferAttribute(points.flat(), 3)
);
// USD uses faceVertexCounts + faceVertexIndices, Three.js wants a flat index array
// For triangulated meshes, this is straightforward:
geometry.setIndex(Array.from(faceVertexIndices));
geometry.computeVertexNormals();
// Create mesh
const material = new THREE.MeshStandardMaterial({ color: 0x888888 });
const mesh = new THREE.Mesh(geometry, material);
// Apply transform
const xform = getWorldTransform(prim);
mesh.matrix.fromArray(xform);
mesh.matrixAutoUpdate = false;
scene.add(mesh);
}
}
function getWorldTransform(prim): number[] {
// Simplified: in practice you'd compose parent transforms
const xformOp = prim.getAttribute('xformOp:transform');
if (xformOp?.value) {
return xformOp.value.flat();
}
return [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1];
}Contoh ini disederhanakan. Kode nyata perlu menangani penumpukan transformasi (prim USD dapat memiliki beberapa atribut xformOp yang dikomposisikan bersama), subdivision surface (mesh dengan subdivisi catmullClark memerlukan proses subdivisi yang sebenarnya), material (parameter UsdPreviewSurface dipetakan ke MeshStandardMaterial milik Three.js), tekstur (path aset perlu di-resolve dan dimuat), serta animasi skeletal (binding UsdSkel perlu dikonversi menjadi SkinnedMesh).
Jika Anda ingin melihat bagaimana semua ini bekerja bersama, lihat @cinevva/usdjs-viewer. Ini adalah implementasi referensi yang lengkap.
Komposisi Lebih Penting daripada yang Anda Kira
Mengurai satu file USDC adalah bagian yang mudah. Scene USD di dunia nyata menggunakan komposisi, yang berarti sublayer, referensi, payload, dan varian bekerja bersama.
Bayangkan sebuah model mobil. Bodi mereferensikan body.usdc, roda mereferensikan wheel.usdc, dan tersedia varian untuk konfigurasi "sport" serta "sedan". Scene akhir dirakit dari semua bagian tersebut saat runtime.
Berikut cara menanganinya:
import { UsdStage, parseUsdcToLayer, FetchResolver } from '@cinevva/usdjs';
// Create a resolver that knows how to fetch referenced files
const resolver = new FetchResolver({
baseUrl: '/assets/',
});
// Open with composition
const stage = UsdStage.open(rootLayer, { resolver });
// Set a variant selection
stage.setVariantSelection('/Car', 'bodyStyle', 'sport');
// Now traverse the composed scene
for (const prim of stage.traverse()) {
// You'll see resolved references and the selected variant
}Resolver mengambil file USDC yang direferensikan sesuai kebutuhan. Komposisi berlangsung di JavaScript dan menghasilkan scene yang telah diratakan, seperti yang akan Anda dapatkan dari usdcat --flatten.
Performa
Mari jujur: mengurai USDC di JavaScript lebih lambat daripada C++ native. Itulah kompromi karena tidak menggunakan WASM dan langkah build tambahan.
Dalam praktiknya, untuk kasus penggunaan web umum dengan model berukuran di bawah 10 MB dan kurang dari 100 ribu segitiga, proses parsing memerlukan 50–200 md pada perangkat keras modern. Waktu tersebut masih wajar untuk pemuatan awal jika Anda menampilkan indikator pemuatan.
Anda dapat mempercepatnya. Jangan memuat seluruh scene di awal. Muat layer root, render apa yang tersedia, lalu ambil payload sesuai kebutuhan. Pindahkan proses parsing ke Web Worker agar UI tetap responsif. Cache layer yang telah diurai di IndexedDB agar kunjungan berikutnya berlangsung seketika. Tampilkan pratinjau beresolusi rendah selagi scene lengkap di-stream.
Berikut konfigurasi Web Worker:
// worker.ts
import { parseUsdcToLayer } from '@cinevva/usdjs';
self.onmessage = async (e) => {
const { buffer, identifier } = e.data;
const layer = parseUsdcToLayer(buffer, { identifier });
// Serialize layer data (not the methods)
const data = serializeLayer(layer);
self.postMessage(data);
};
// main.ts
const worker = new Worker(new URL('./worker.ts', import.meta.url));
worker.postMessage({ buffer, identifier: 'model.usdc' });
worker.onmessage = (e) => {
const layerData = e.data;
// Build Three.js scene from layerData
};Apa yang Berfungsi dan Apa yang Tidak
Sebagian besar hal umum sudah berfungsi. Geometri (mesh, titik, dan kurva) diurai dengan benar, termasuk array vertex terkompresi. Transformasi berfungsi dengan penumpukan xformOp penuh. Material dipetakan dari UsdPreviewSurface ke PBR. Komposisi menangani sublayer, referensi, payload, varian, dan pewarisan. Tekstur dapat di-resolve dan dimuat jika Anda menyediakan resolver. Animasi skeletal dasar berfungsi untuk rig karakter umum.
Namun, masih ada kekurangan. Ini bukan implementasi Hydra, jadi Anda bertanggung jawab mengonversi data USD ke mesin rendering apa pun yang digunakan. Tidak tersedia API skema bertipe seperti UsdGeomMesh dengan berbagai metode praktis. Anda bekerja dengan prim dan atribut generik. Beberapa fitur komposisi seperti specializes, relocates, dan value clips belum diimplementasikan. Jaringan material kompleks di luar UsdPreviewSurface sederhana memerlukan penanganan khusus.
Kapan Sebaiknya Menggunakan Ini
Solusi ini cocok ketika Anda perlu memuat file USD dalam aplikasi web tanpa langkah build WASM, ketika pipeline Anda menghasilkan USDC dan Anda tidak ingin mengonversinya ke glTF, ketika Anda membuat penampil atau editor USD untuk browser, atau ketika Anda ingin memeriksa struktur USD alih-alih sekadar merendernya.
Gunakan solusi lain jika Anda memerlukan kesetaraan penuh dengan OpenUSD untuk setiap kasus khusus, jika scene Anda sangat besar (100 MB+) dan memerlukan performa native, atau jika Anda sudah menggunakan build WASM dan kompleksitasnya masih dapat diterima untuk proyek Anda.
Sumber Daya
Ketiga paket tersebut adalah @cinevva/usdjs untuk parsing dan komposisi inti, @cinevva/usdjs-viewer untuk penampil browser berbasis Three.js, serta @cinevva/usdjs-renderer untuk rendering PNG headless dalam pengujian.
Untuk dokumentasi, lihat Referensi API usdjs, Spesifikasi OpenUSD Pixar, dan Dokumentasi Three.js.
Jika Anda ingin memahami bagaimana parser USDC dipetakan ke implementasi Pixar, lihat src/usdc/PIXAR_PARITY.md di repositori usdjs.
Cobalah
Cara tercepat untuk melihatnya bekerja adalah dengan mengunjungi demo usdjs-viewer dan meletakkan file USDC di sana.
Untuk mengintegrasikannya ke proyek Anda sendiri, instal paket tersebut dan mulai dengan contoh kode di atas:
npm install @cinevva/usdjsUSD di browser itu memungkinkan. Ini tidak selalu menjadi pilihan yang tepat, tetapi saat Anda membutuhkannya, parsing JavaScript murni berarti satu langkah build dan satu dependensi lebih sedikit untuk dipikirkan.
Terkait
- Dasar-dasar WebGL untuk pengembang game — API rendering yang menggerakkan scene Three.js
- Stack Teknologi Game Web pada 2026 — posisi Three.js dalam lanskap WebGL/WebGPU/Wasm
- Teknologi Dunia Terbuka 3D di Browser — streaming aset 3D, termasuk USD, untuk dunia terbuka
- Tempat Menemukan Aset Game Gratis — sumber model 3D yang kompatibel dengan Three.js