Fitur bekerja dengan Mesh in Unity

Grafik komputer, seperti yang Anda tahu, adalah dasar dari industri game. Dalam proses pembuatan konten grafis, kami pasti akan menghadapi kesulitan yang terkait dengan perbedaan dalam presentasinya di lingkungan pembuatan dan dalam aplikasi. Untuk kesulitan-kesulitan ini ditambahkan risiko kecerobohan manusia yang sederhana. Mengingat skala pengembangan game, masalah seperti itu sering muncul atau dalam jumlah besar.

Memerangi kesulitan seperti itu membuat kami berpikir tentang otomatisasi dan menulis artikel tentang topik ini. Sebagian besar materi akan berurusan dengan bekerja dengan Unity 3D , karena ini adalah alat pengembangan utama di Plarium Krasnodar. Selanjutnya, model dan tekstur 3D akan dianggap sebagai konten grafis.

Pada artikel ini, kita akan berbicara tentang fitur mengakses data yang mewakili objek 3D di Unity . Materi akan berguna terutama untuk pemula, serta para pengembang yang jarang berinteraksi dengan representasi internal dari model tersebut.



Tentang model 3D di Unity - untuk yang terkecil




Dalam pendekatan standar, Unity menggunakan komponen MeshFilter dan MeshRenderer untuk membuat model. MeshFilter mengacu pada aset Mesh yang mewakili model. Untuk sebagian besar shader, informasi geometri adalah komponen minimum wajib untuk rendering model di layar. Data pemindaian tekstur dan tulang animasi mungkin tidak tersedia jika mereka tidak terlibat. Bagaimana kelas ini diimplementasikan di dalam dan bagaimana semuanya disimpan di sana adalah misteri untuk jumlah uang ke tujuh dalam tujuh meterai.

Di luar, jala sebagai objek menyediakan akses ke kumpulan data berikut:

  • simpul - seperangkat posisi simpul geometri dalam ruang tiga dimensi dengan asalnya sendiri;
  • normals, garis singgung - set vektor normal dan garis singgung ke simpul yang biasanya digunakan untuk menghitung pencahayaan;
  • uv, uv2, uv3, uv4, uv5, uv6, uv7, uv8 - set koordinat untuk pemindaian tekstur;
  • warna, colors32 - set nilai warna simpul, contoh buku teks yang adalah untuk mencampur tekstur dengan topeng;
  • bindposes - set matriks untuk memposisikan simpul relatif terhadap tulang;
  • boneWeights - koefisien pengaruh tulang di atas;
  • segitiga - satu set indeks simpul diproses 3 pada satu waktu; setiap tripel tersebut merepresentasikan poligon (dalam hal ini, segitiga) dari model.

Akses ke informasi tentang simpul dan poligon diimplementasikan melalui properti yang sesuai, masing-masing mengembalikan array struktur. Untuk orang yang tidak membaca dokumentasi jarang bekerja dengan jerat di Unity , mungkin tidak jelas bahwa setiap kali data vertex diakses, salinan dari set yang sesuai dibuat dalam memori dalam bentuk array dengan panjang sama dengan jumlah simpul. Nuansa ini dipertimbangkan dalam blok kecil dokumentasi . Komentar tentang properti kelas Mesh yang disebutkan di atas juga memperingatkan tentang hal ini. Alasan untuk perilaku ini adalah fitur arsitektur Unity dalam konteks runtime Mono . Secara skematis, ini dapat direpresentasikan sebagai berikut:



Inti mesin (UnityEngine (asli)) diisolasi dari skrip pengembang, dan akses ke fungsinya diimplementasikan melalui perpustakaan UnityEngine (C #). Sebenarnya, ini adalah adaptor, karena sebagian besar metode berfungsi sebagai lapisan untuk menerima data dari kernel. Pada saat yang sama, kernel dan sisanya, termasuk skrip Anda, berputar di bawah proses yang berbeda dan bagian skrip hanya tahu daftar perintah. Dengan demikian, tidak ada akses langsung ke memori yang digunakan oleh kernel dari skrip.

Tentang akses ke data internal, atau seberapa buruk hal itu


Untuk menunjukkan seberapa buruknya hal itu, mari kita menganalisis jumlah memori yang dihapus oleh Pengumpul Sampah menggunakan contoh dari dokumentasi. Untuk kesederhanaan pembuatan profil, bungkus kode yang sama dalam metode Pembaruan.

public class MemoryTest : MonoBehaviour { public Mesh Mesh; private void Update() { for (int i = 0; i < Mesh.vertexCount; i++) { float x = Mesh.vertices[i].x; float y = Mesh.vertices[i].y; float z = Mesh.vertices[i].z; DoSomething(x, y, z); } } private void DoSomething(float x, float y, float z) { //nothing to do } } 

Kami menjalankan skrip ini dengan primitif standar - bola (515 simpul). Dengan menggunakan alat Profiler , pada tab Memory , Anda dapat melihat berapa banyak memori yang telah ditandai untuk pengumpulan sampah di setiap frame. Di mesin kami, nilai ini ~ 9,2 Mb.



Ini cukup banyak bahkan untuk aplikasi yang dimuat, dan di sini kami meluncurkan adegan dengan satu objek tempat script paling sederhana dipasang.

Penting untuk menyebutkan fitur .Net compiler dan optimisasi kode. Melalui rantai panggilan, Anda akan menemukan bahwa panggilan Mesh.vertices mengharuskan memanggil metode eksternal mesin. Ini mencegah kompilator mengoptimalkan kode di dalam metode Pembaruan () kami , meskipun fakta bahwa DoSomething () kosong dan variabel x, y, z tidak digunakan untuk alasan ini.

Sekarang kita cache array posisi di awal.

 public class MemoryTest : MonoBehaviour { public Mesh Mesh; private Vector3[] _vertices; private void Start() { _vertices = Mesh.vertices; } private void Update() { for (int i = 0; i < _vertices.Length; i++) { float x = _vertices[i].x; float y = _vertices[i].y; float z = _vertices[i].z; DoSomething(x, y, z); } } private void DoSomething(float x, float y, float z) { //nothing to do } } 



Rata-rata 6 Kb. Hal lain!

Fitur ini menjadi salah satu alasan mengapa kami harus menerapkan struktur kami sendiri untuk menyimpan dan memproses data mesh.

Bagaimana kita melakukannya?


Selama pengerjaan proyek besar, muncul ide untuk membuat alat untuk analisis dan pengeditan konten grafis yang diimpor. Kami akan membahas metode analisis dan transformasi dalam artikel berikut. Sekarang mari kita lihat struktur data yang kami putuskan untuk ditulis demi kenyamanan penerapan algoritma, dengan mempertimbangkan fitur akses ke informasi tentang mesh.

Awalnya, struktur ini terlihat seperti ini:



Di sini, kelas CustomMesh mewakili jala itu sendiri. Secara terpisah, dalam bentuk Utilitas, kami menerapkan konversi dari UntiyEngine.Mesh dan sebaliknya. Jala didefinisikan oleh array segitiga. Setiap segitiga persis mengandung tiga tepi, yang pada gilirannya ditentukan oleh dua simpul. Kami memutuskan untuk menambahkan ke simpul hanya informasi yang kami butuhkan untuk analisis, yaitu: posisi, normal, dua saluran pemindaian tekstur ( uv0 untuk tekstur utama, uv2 untuk penerangan) dan warna.

Setelah beberapa waktu, muncul kebutuhan untuk naik hierarki. Sebagai contoh, untuk mencari tahu dari segitiga yang mana jala itu berasal. Selain itu, penurunan peringkat dari CustomMesh ke Vertex tampak megah, dan jumlah duplikat nilai yang tidak masuk akal dan signifikan membuat saya gugup. Untuk alasan ini, struktur harus dirancang ulang.



CustomMeshPool mengimplementasikan metode untuk manajemen yang nyaman dan akses ke semua CustomMesh yang diproses. Karena bidang MeshId , setiap entitas memiliki akses ke informasi seluruh mesh. Struktur data ini memenuhi persyaratan untuk tugas-tugas awal. Mudah untuk diperluas dengan menambahkan set data yang sesuai ke CustomMesh dan metode yang diperlukan untuk Vertex .

Perlu dicatat bahwa pendekatan ini tidak optimal dalam kinerja. Pada saat yang sama, sebagian besar algoritma yang telah kami implementasikan difokuskan untuk menganalisis konten dalam editor Unity , itulah sebabnya Anda tidak perlu sering memikirkan jumlah memori yang digunakan. Untuk alasan ini, kami benar-benar men-cache semua yang mungkin terjadi. Kami pertama-tama menguji algoritma yang diterapkan, dan kemudian memperbaiki metode-metodenya dan, dalam beberapa kasus, menyederhanakan struktur data untuk mengoptimalkan runtime.

Itu saja untuk saat ini. Pada artikel berikutnya, kita akan berbicara tentang cara mengedit model 3D yang sudah ditambahkan ke proyek, dan kita akan menggunakan struktur data yang dipertimbangkan.

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


All Articles