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) {
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) {

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.