Dasar-dasar format GLTF dan GLB, bagian 1

Apa itu GLTF dan GLB?


GLTF (GL Transmission Format) adalah format file untuk menyimpan adegan dan model 3D, yang sangat mudah dimengerti (strukturnya ditulis dalam standar JSON), dapat diperluas dan mudah berinteraksi dengan teknologi web modern. Format ini memampatkan adegan tiga dimensi dengan baik dan meminimalkan pemrosesan runtime aplikasi menggunakan WebGL dan API lainnya. GLTF sekarang secara aktif dipromosikan oleh Khronos Group sebagai JPEG dari dunia 3D. GLTF versi 2.0 saat ini digunakan. Ada juga versi biner dari format ini yang disebut GLB, satu-satunya perbedaan adalah bahwa semuanya disimpan dalam satu file dengan ekstensi GLB.


Artikel ini adalah bagian 1 dari 2. Di dalamnya, kita akan mempertimbangkan artefak format dan atributnya seperti Scene, Node, Buffer, BufferView, Accessor dan Mesh . Dan di artikel kedua kita akan melihat sisanya: Bahan, Tekstur, Animasi, Kulit dan Kamera. Informasi format yang lebih umum dapat ditemukan di sini .
Jika Anda ingin bekerja secara pribadi dengan format ini saat melihat artikel, Anda dapat mengunduh model GLTF 2.0 dari repositori Khronos resmi di GitHub


gambar


Masalah dan solusinya


Awalnya, format GLTF disusun oleh Khronos Group sebagai solusi untuk mentransmisikan konten 3D melalui Internet dan dirancang untuk meminimalkan jumlah importir dan konverter, berbagai jenis yang dibuat ketika bekerja dengan API grafik.


gambar

Saat ini, GLTF dan saudara binernya GLB digunakan sebagai format terpadu dalam program CAD (Autodesk Maya, Blender, dll.), Dalam mesin game (Unreal Engine, Unity, dll.), Aplikasi AR / VR, dan layanan sosial. jaringan, dll.


Perwakilan dari Grup Khronos menyatakan sebagai berikut:


  1. GLTF bersifat universal - dapat digunakan sama baiknya untuk geometri sederhana maupun untuk adegan kompleks dengan animasi, berbagai bahan, dll.
  2. Cukup kompak. Ya, ini bisa diperdebatkan, karena semuanya tergantung pada algoritma konversi, dan saya pribadi tahu kasus di mana GLTF lebih besar dari aslinya, misalnya, file FBX, tetapi dalam kebanyakan kasus itu.
  3. Kemudahan analisis data adalah root plus dari format ini. Hirarki GLTF menggunakan JSON, dan geometri disimpan dalam bentuk biner, tidak perlu decoding!

Mengkoordinasikan sistem dan unit


GLTF menggunakan sistem koordinat tangan kanan, yaitu, produk silang + X dan + Y memberi + Z, di mana + Y adalah sumbu atas. Bagian depan aset 3D GLTF menghadap ke sumbu + Z. Satuan pengukuran untuk semua jarak linear adalah meter, sudut diukur dalam radian, dan rotasi positif benda berlawanan arah jarum jam. Transformasi node dan jalur saluran animasi adalah vektor tiga dimensi atau angka empat dengan tipe data dan semantik berikut:


terjemahan : vektor tiga dimensi yang berisi terjemahan sepanjang sumbu x, y, dan z
rotasi : angka empat (x, y, z, w), di mana w adalah skalar
skala : vektor tiga dimensi yang mengandung faktor penskalaan x, y, dan z
gambar


GLTF - tampilan dalam


Seperti disebutkan di atas, GLTF, sebagai suatu peraturan, terdiri dari 2 file: yang pertama dengan format .gltf, yang menyimpan struktur adegan 3D dalam bentuk JSON dan file ke-2 dengan format .bin, yang menyimpan langsung semua data adegan ini.


Struktur format sangat hierarkis dan memiliki bentuk berikut:


gambar


Berbicara lebih lanjut tentang struktur, saya akan menggunakan contoh file GLTF yang paling sederhana, yang menyimpan 1 segitiga satu sisi dengan bahan default. Jika Anda mau, maka Anda dapat menyalinnya dan menempelkannya ke penampil GLTF untuk "merasakan" isi file secara pribadi. Dalam latihan saya, saya menggunakan yang berbeda, tetapi memutuskan ini , yang menggunakan Three.js di bawah tenda. Juga pilihan yang baik adalah menggunakan Visual Studio Code dengan plugin GLTF. Jadi Anda akan memiliki pilihan segera dari 3 mesin: Babylon.js, Cesium, Three.js


Elemen Scene dan Node


Hal pertama yang pertama adalah simpul utama yang disebut Scene. Ini adalah titik root dalam file tempat semuanya dimulai. Node ini berisi larik adegan yang GLTF simpan dan pilihan yang akan dimuat secara default setelah membuka file. Konten adegan 3D dimulai dengan objek berikutnya, yang disebut "Node". Array adegan dan node disebutkan tidak sia-sia, karena kemampuan untuk menyimpan beberapa adegan dalam satu file diimplementasikan, tetapi dalam praktiknya mereka mencoba untuk menyimpan satu adegan dalam satu file.


{ "scenes" : [ { "nodes" : [ 0 ] } ], "nodes" : [ { "mesh" : 0 } ], "scene": 0 

Setiap node adalah "titik masuk" untuk menggambarkan objek individual. Jika objek tersebut kompleks dan terdiri dari beberapa jerat, maka objek tersebut akan dijelaskan oleh simpul "induk" dan "anak". Sebagai contoh, sebuah mobil, yang terdiri dari badan dan roda, dapat digambarkan sebagai berikut: simpul utama menggambarkan mobil dan, khususnya, tubuhnya. Node ini berisi daftar "child node", yang, pada gilirannya, menjelaskan komponen yang tersisa, seperti, misalnya, roda. Semua elemen akan diproses secara rekursif. Node dapat memiliki animasi TRS (terjemahan, rotasi, skala alias perpindahan, rotasi, dan penskalaan). Selain fakta bahwa transformasi tersebut secara langsung mempengaruhi mesh itu sendiri, mereka juga mempengaruhi node anak dengan cara yang persis sama. Selain semua hal di atas, saya pikir perlu menyebutkan bahwa "kamera" internal, jika ada, yang bertanggung jawab untuk menampilkan objek dalam bingkai untuk pengguna, juga melekat pada objek Node. Objek merujuk satu sama lain menggunakan atribut yang sesuai: scene memiliki atribut node, objek node memiliki atribut mesh. Untuk pemahaman yang lebih sederhana, semua hal di atas diilustrasikan dalam gambar berikut.


gambar


Buffer, BufferView, dan Accessor


Objek penyangga berarti penyimpanan biner, tidak diolah, data tanpa struktur, tanpa warisan, tanpa nilai. Buffer menyimpan informasi tentang geometri, animasi, dan skinning. Keuntungan utama dari data biner adalah bahwa ia diproses dengan sangat efisien oleh GPU, seperti tidak memerlukan parsing tambahan, kecuali, mungkin, dekompresi. Data dalam buffer dapat ditemukan oleh atribut URI, yang dengan jelas membuatnya jelas di mana data berada dan hanya ada 2 opsi: apakah data disimpan dalam file eksternal dengan format .bin, atau mereka tertanam di dalam JSON itu sendiri. Dalam kasus pertama, URI berisi tautan ke file eksternal, dalam hal ini, folder tempat file GLTF berada dianggap sebagai root. Dalam kasus kedua, file akan memiliki format .glb, yang merujuk kita ke yang lebih ringkas, dalam hal jumlah file, saudara kembar GLTF, format GLB. Data dalam file biner disimpan apa adanya, byte-by-byte.



JSON dalam contoh segitiga kami akan terlihat seperti ini:
Contoh buffer yang disandikan base64:


 "buffers" : [ { "uri" : "data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAA=", "byteLength" : 44 } ], 

Jika Anda memiliki file eksternal, maka JSON akan mengonversi tampilan menjadi sebagai berikut:


 "buffers" : [ { "uri" : "duck.bin", "byteLength" : 102040 } ], 

Blok Buffer juga memiliki atribut byteLength tambahan, yang menyimpan nilai ukuran buffer.


Langkah pertama dalam menyusun data dari buffer adalah objek BufferView. BufferView dapat disebut "irisan" informasi dari Buffer, yang ditandai dengan pergeseran byte tertentu dari awal buffer. "Iris" ini dijelaskan menggunakan 2 atribut: "pergeseran" dihitung dari awal buffer baca dan panjang irisan itu sendiri. Contoh sederhana beberapa objek BufferView untuk menggambarkan penggunaannya berdasarkan contoh kami:


  "bufferViews" : [ { "buffer" : 0, "byteOffset" : 0, "byteLength" : 6, "target" : 34963 }, { "buffer" : 0, "byteOffset" : 8, "byteLength" : 36, "target" : 34962 } ], 

Seperti yang Anda lihat, contoh ini berisi 4 atribut utama:


  1. Buffer menunjuk ke indeks buffer (nomor urut dalam array buffer dimulai dari 0).
  2. byteOffset - mendefinisikan "shift" asal dalam byte untuk "slice" ini
  3. byteLength - mendefinisikan panjang "slice"
  4. target - mendefinisikan jenis data yang terkandung dalam bufferView
    BufferView pertama berisi 6 byte pertama buffer dan tidak memiliki pergeseran. Dengan "cut" kedua, semuanya menjadi sedikit lebih rumit: seperti yang Anda lihat, shiftnya 8m byte, bukan 6 yang diharapkan. 2 byte ini kosong dan ditambahkan selama proses pembuatan buffer berkat proses yang disebut "padding". Perlu untuk nilai untuk menyesuaikan nilai byte batas ke 4 byte. Trik ini diperlukan untuk membaca data dari buffer dengan lebih cepat dan lebih mudah.

gambar


Perlu dikatakan beberapa kata tentang atribut target. Ini digunakan untuk mengklasifikasikan jenis informasi yang dirujuk oleh bufferView. Hanya ada 2 opsi: baik itu nilai 34962, yang digunakan untuk merujuk pada atribut vertex (atribut vertex - 34962 - ARRAY_BUFFER) atau 34963, yang digunakan untuk indeks titik (indeks titik - 34963 - ELEMENT_ARRAY_BUFFER). Sentuhan terakhir untuk memahami dan menyusun semua informasi dalam Buffer adalah objek Accessor.


Accessor adalah objek yang mengakses BufferView dan berisi atribut yang menentukan jenis dan lokasi data dari BufferView. Tipe data accessor dikodekan dalam tipe dan tipe komponen. Nilai atribut type adalah string dan memiliki nilai berikut: SCALAR untuk nilai skalar, VEC3 untuk vektor 3 dimensi dan MAT4 untuk matriks 4x4 atau angka empat, yang digunakan untuk menggambarkan rotasi.


Jenis Komponen pada gilirannya menunjukkan jenis komponen dari data ini. Ini adalah konstanta GL, yang dapat memiliki nilai seperti 5126 (FLOAT) atau 5123 (UNSIGNED_SHORT), misalnya, untuk menunjukkan bahwa elemen memiliki titik apung, dll.


Berbagai kombinasi properti ini dapat digunakan untuk menggambarkan tipe data yang berubah-ubah. Contoh berdasarkan segitiga kami.


  "accessors" : [ { "bufferView" : 0, "byteOffset" : 0, "componentType" : 5123, "count" : 3, "type" : "SCALAR", "max" : [ 2 ], "min" : [ 0 ] }, { "bufferView" : 1, "byteOffset" : 0, "componentType" : 5126, "count" : 3, "type" : "VEC3", "max" : [ 1.0, 1.0, 0.0 ], "min" : [ 0.0, 0.0, 0.0 ] } ], 

Mari kita menganalisis atribut yang diwakili dalam JSON:


  1. bufferView - menunjukkan nomor urut BufferView dari array BufferView yang digunakan Accessor. BufferView, pada gilirannya, menyimpan informasi tentang indeks.
  2. byteOffset - byte shift untuk mulai membaca data dari Accessor saat ini. Beberapa objek Accessor dapat merujuk satu BufferView.
  3. componentType adalah konstanta yang menunjukkan jenis elemen. Ini dapat memiliki nilai 5123, yang sesuai dengan tipe data UNSIGNED_SHORT, atau 5126 untuk FLOAT.
  4. count - menampilkan berapa banyak elemen yang disimpan dalam buffer.
  5. type - mendefinisikan tipe data: skalar, vektor, matriks.
  6. atribut maksimum dan minimum yang menentukan nilai minimum dan maksimum posisi elemen-elemen ini dalam ruang.

Mesh


Objek jerat berisi informasi tentang jerat yang terletak di tempat kejadian. Satu node (objek node) hanya dapat menyimpan 1 mesh. Setiap objek bertipe mesh berisi array bertipe mesh. Priitif, pada gilirannya, primitif adalah objek primitif (misalnya, segitiga) yang terdiri dari mesh itu sendiri. Objek ini mengandung banyak atribut tambahan, tetapi semua ini melayani satu tujuan - penyimpanan informasi yang benar tentang tampilan objek. Atribut utama dari mesh:


  1. POSISI - posisi simpul sepanjang sumbu XYZ
  2. NORMAL - Normalisasi Vertikal XYZ Normal
  3. TANGENT - XYZW garis singgung dari simpul. W menunjukkan di mana tangen diarahkan dan memiliki nilai +1 atau -1.
  4. TEXCOORD_0 - Koordinat tekstur UV. Beberapa set dapat disimpan.
  5. COLOR_0 - RGB atau warna RGBA dari simpul.
  6. JOINTS_0 - atribut ini berisi indeks sendi / Sendi dari array sendi yang sesuai, yang harus memengaruhi vertex (vertex).
  7. WEIGHTS_0 - data atribut ini menentukan bobot yang menunjukkan seberapa besar sendi mempengaruhi verteks.
  8. bobot - atribut yang bertanggung jawab atas bobot morphing.
  9. material - berisi indeks, yang merupakan jumlah material dalam array Material

Objek ini akan memiliki bentuk berikut untuk kasus kami:


  "meshes" : [ { "primitives" : [ { "attributes" : { "POSITION" : 1 }, "indices" : 0 } ] } ], 

Sayangnya, karena pembatasan, semua materi tidak cocok menjadi satu artikel, sehingga sisanya dapat ditemukan di artikel kedua , di mana kami akan mempertimbangkan artefak yang tersisa: Bahan, Tekstur, Animasi, Kulit dan Kamera , serta mengumpulkan file GLTF yang berfungsi minimal.


Lanjutan di bagian 2: https://habr.com/en/post/448298/

Source: https://habr.com/ru/post/id448220/


All Articles