Banyak objek dalam tiga.js memiliki properti Needsupdate, dan mereka jarang ditulis dalam dokumen (tetapi tidak ada banyak dokumen dalam tiga.js, dan banyak masalah masih harus bergantung pada masalah pada GitHub). Mereka tidak tahu cara menulis ini di berbagai tutorial online, karena untuk program pengantar sederhana, properti ini tidak dapat digunakan.
Jadi untuk apa atribut ini digunakan? Singkatnya, saya memberi tahu renderer bahwa saya harus memperbarui cache dalam bingkai ini. Meskipun sangat mudah digunakan sebagai bit bendera, karena Anda perlu tahu mengapa Anda perlu memperbarui cache dan cache mana yang akan diperbarui, masih perlu memahaminya dengan cermat.
Mengapa perlu ditanggapiPertama -tama, mari kita lihat mengapa cache diperlukan. Keberadaan cache umumnya untuk mengurangi jumlah waktu transmisi data, sehingga mengurangi waktu yang dihabiskan untuk transmisi data. Di sini, juga benar bahwa tidak mudah bagi suatu objek (mesh) untuk berhasil ditampilkan di layar pada akhirnya. Itu perlu ditransfer ke medan perang tiga kali.
Yang pertama adalah membaca semua data vertex dan data tekstur dari disk lokal ke dalam memori melalui program.
Kemudian, setelah program melakukan pemrosesan yang tepat dalam memori, ia perlu mentransfer data vertex dan data tekstur objek yang perlu ditarik ke layar ke memori video.
Akhirnya, ketika merender setiap bingkai, data vertex dan data tekstur dalam memori video disiram ke GPU untuk perakitan dan gambar.
Menurut model transmisi data seperti piramida, langkah pertama jelas paling lambat. Jika ditransmisikan melalui jaringan di lingkungan seperti WebGL, itu akan lebih lambat. Yang kedua adalah waktu dari memori ke memori video, yang akan menjadi tes data sederhana nanti.
Lalu ada frekuensi penggunaan tiga langkah ini. Untuk skenario kecil, langkah pertama adalah satu kali, yaitu, setiap kali program diinisialisasi, semua data skenario akan dimuat ke dalam memori. Untuk skenario besar, beberapa pemuatan asinkron dapat dilakukan, tetapi saat ini bukan masalah yang kami pertimbangkan. Frekuensi langkah kedua harus menjadi hal yang paling penting untuk dibicarakan saat ini. Pertama, tulis program sederhana untuk menguji konsumsi yang disebabkan oleh melakukan langkah transmisi ini.
var canvas = document.createElement ('Canvas');
var _gl = canvas.getContext ('Experimental-Webgl');
var simpul = [];
untuk (var i = 0; i <1000*3; i ++) {
vertices.push (i * math.random ());
}
var buffer = _gl.createBuffer ();
console.profile ('buffer_test');
bindbuffer ();
console.profileEnd ('buffer_test');
function bindBuffer () {
untuk (var i = 0; i <1000; i ++) {
_gl.bindbuffer (_gl.array_buffer, buffer);
_gl.bufferData (_gl.array_buffer, float32Array baru (simpul), _gl.static_draw);
}
}
Mari kita jelaskan program ini terlebih dahulu. Simpul adalah array yang menghemat simpul. Di sini, 1.000 simpul dihasilkan secara acak. Karena setiap titik memiliki tiga koordinat x, y, dan z, diperlukan array dengan ukuran 3000. Perintah _gl.createBuffer membuka cache untuk menyimpan data verteks di memori video, dan kemudian menggunakan _gl.bufferData untuk mentransfer data vertex yang dihasilkan dari memori ke memori video. Di sini kami mengasumsikan bahwa ada 1000 objek dengan 1000 simpul dalam suatu adegan, setiap simpul adalah 3 32-bit 4 byte data float. Hitung data hampir 1000 x 1000 x 12 = 11m. Profil membutuhkan waktu sekitar 15ms. Di sini kita mungkin melihat bagaimana 15ms hanya sedikit waktu. Namun, untuk program real-time, jika Anda ingin memastikan frame rate 30fps, waktu yang diperlukan untuk setiap bingkai harus dikontrol sekitar 30 ms. Bagaimana mungkin setengah dari waktu untuk hanya melakukan transmisi data? Anda harus tahu bahwa kepala besar harus menjadi operasi menggambar di GPU dan berbagai pemrosesan di CPU, dan Anda harus pelit dengan setiap langkah operasi di seluruh proses rendering.
Oleh karena itu, jumlah transmisi dalam langkah ini harus diminimalkan. Bahkan, ini dapat digunakan untuk mentransfer semua data vertex dan data tekstur dari memori ke memori video saat dimuat. Inilah yang dilakukan Three.js sekarang. Data verteks dari objek yang perlu ditarik ditransfer ke memori video untuk pertama kalinya, dan cache buffer ke geometri .__ WebGlVertExBuffer. Setelah itu, setiap kali Anda menggambar, Anda akan menilai properti geometri Verticesneedupdate. Jika Anda tidak perlu memperbarui, gunakan cache saat ini secara langsung. Jika Anda melihat bahwa VerticesNeedupate benar, data verteks dalam geometri akan ditransfer ke geometri .__ WebGlVertExBuffer. Secara umum, kita tidak memerlukan langkah ini untuk objek statis. Namun, jika kita menemukan objek yang sering berubah, seperti menggunakan simpul sebagai sistem partikel, dan mesh yang menggunakan animasi kerangka, objek -objek ini akan mengubah simpul mereka di setiap bingkai, sehingga setiap bingkai perlu mengatur properti VerticesNeedUpdate untuk benar untuk memberi tahu penerima bahwa saya perlu mengirimkan kembali data!
Bahkan, dalam program WebGL, lebih banyak posisi vertex akan diubah di vertex shader untuk menyelesaikan efek partikel dan animasi kerangka. Meskipun lebih mudah untuk memperluas jika ditempatkan di sisi CPU untuk menghitung, karena keterbatasan daya komputasi JavaScript, lebih banyak operasi komputasi ini akan ditempatkan di sisi GPU. Dalam hal ini, tidak perlu mengubah ulang data vertex, sehingga kasus di atas tidak banyak digunakan dalam program aktual, dan ini lebih tentang memperbarui tekstur dan cache material.
Kasus di atas terutama menjelaskan skenario di mana data verteks ditransmisikan. Selain data vertex, ada juga kepala besar yang merupakan teksturnya. Tekstur format R8G8B8A8 berukuran 1024*1024 harus menempati ukuran memori hingga 4m, jadi lihat contoh berikut.
var canvas = document.createElement ('Canvas');
var _gl = canvas.getContext ('Experimental-Webgl');
var tekstur = _gl.createTexture ();
var img = gambar baru;
img.onload = function () {
console.profile ('Tes Tekstur');
bindTexture ();
console.profileEnd ('Tes Tekstur');
}
img.src = 'test_tex.jpg';
fungsi bindTexture () {
_gl.bindTexture (_gl.texture_2d, tekstur);
_gl.teximage2d (_gl.texture_2d, 0, _gl.rgba, _gl.rgba, _gl.unsigned_byte, img);
}
Tidak perlu mengulangi 1000 kali sesat di sini. Dibutuhkan 30ms untuk mengirimkan tekstur 10241024 sekaligus, dan gambar 256256 hampir 2ms. Oleh karena itu, dalam tiga.js, tekstur hanya boleh ditransmisikan sekali di awal. Setelah itu, jika tekstur. Properti yang dibutuhkan tidak secara manual diatur ke True, tekstur yang telah ditransfer ke memori video akan digunakan secara langsung.
Cache apa yang perlu diperbaruiDi atas menjelaskan mengapa tiga.js perlu menambahkan atribut kebutuhan kebutuhan tersebut melalui dua kasus. Selanjutnya, daftar beberapa skenario untuk diketahui dalam keadaan apa yang Anda butuhkan untuk memperbarui cache ini secara manual.
Memuat tekstur asinkronIni adalah lubang kecil, karena gambar front-end dimuat secara tidak sinkron. Jika Anda menulis Texture.NeedSupDate = true langsung setelah membuat IMG, renderer Three.js akan menggunakan _gl.teximage2d untuk mentransfer data tekstur kosong ke memori video dalam bingkai ini, dan kemudian mengatur bendera ini ke false. Kemudian, ketika gambar dimuat, data memori video tidak akan diperbarui. Oleh karena itu, Anda harus menunggu seluruh gambar dimuat di acara Onload sebelum menulis tekstur.
Tekstur videoSebagian besar tekstur seperti halnya di atas untuk memuat dan mengirimkan gambar secara langsung, tetapi tidak untuk tekstur video, karena video tersebut adalah aliran gambar, dan gambar yang akan ditampilkan di setiap bingkai berbeda, jadi Anda perlu mengatur NeedpDate ke True untuk setiap bingkai untuk memperbarui data tekstur di kartu grafis.
Gunakan Render BufferBuffer render adalah objek yang relatif istimewa. Secara umum, program akan menyiram langsung ke layar setelah seluruh adegan ditarik. Namun, jika ada lebih banyak pemrosesan pos atau XXX berbasis layar (seperti kejadian ambien berbasis layar), adegan perlu ditarik terlebih dahulu pada buffer render. Buffer ini sebenarnya adalah tekstur, tetapi dihasilkan oleh gambar sebelumnya, tidak dimuat dari disk. Ada objek tekstur khusus WebGlrenderTarget di Three.js untuk menginisialisasi dan menyimpan renderBuffer. Tekstur ini juga perlu diatur ke True di setiap bingkai.
Kebutuhan materialBahan dijelaskan dalam tiga.js melalui tiga. Bahan. Faktanya, materi tidak memiliki banyak data untuk ditransmisikan, tetapi mengapa Anda perlu membuat persyaratan? Di sini saya akan berbicara tentang shader. Shader diterjemahkan sebagai shader, yang menyediakan kemungkinan simpul pemrograman dan piksel dalam GPU. Ada istilah yang teduh dalam lukisan untuk mewakili metode lukisan terang dan gelap. Naungan dalam GPU serupa. Cahaya dan kegelapan cahaya dihitung oleh program untuk mengekspresikan bahan suatu objek. OK, karena Shader adalah program yang berjalan di GPU, seperti semua program, perlu melakukan operasi kompilasi dan penghubung. Di WebGL, program shader dikompilasi saat runtime, yang tentu saja membutuhkan waktu, jadi yang terbaik adalah disusun dan dijalankan hingga akhir program. Jadi ketika materi diinisialisasi dalam tiga.js, program shader dikompilasi dan ditautkan dan objek program yang diperoleh setelah tautan kompilasi di -cache. Secara umum, suatu bahan tidak perlu mengkompilasi ulang seluruh shader lagi. Untuk menyesuaikan bahan, Anda hanya perlu memodifikasi parameter seragam shader. Namun, jika Anda mengganti seluruh materi, seperti mengganti pong shader asli dengan Lambert Shader, Anda perlu mengatur materi. Namun, situasi ini jarang terjadi, dan yang lebih umum adalah yang disebutkan di bawah ini.
Tambahkan dan Lepaskan LampuIni harus lebih umum dalam adegan. Mungkin banyak orang yang baru saja mulai menggunakan tiga.js akan jatuh ke dalam lubang ini. Setelah menambahkan cahaya ke adegan secara dinamis, mereka menemukan bahwa cahaya tidak berfungsi. Namun, saat menggunakan shader yang dibangun di tiga.js, misalnya, Phong, Lambert, melihat kode sumber di renderer, Anda akan menemukan bahwa tiga.js menggunakan #define dalam kode shader bawaan untuk mengatur jumlah lampu dalam adegan. Nilai #define ini diperoleh dengan string splicing shader setiap kali materi diperbarui. Kode adalah sebagai berikut.
"#define max_dir_lights" + parameter.maxdirlights,
"#define max_point_lights" + parameter.maxpointlights,
"#define max_spot_lights" + parameter.maxspotlights,
"#define max_hemi_lights" + parameter.maxhemilights,
Memang, metode penulisan ini dapat secara efektif mengurangi penggunaan register GPU. Jika hanya ada satu lampu, Anda hanya dapat menyatakan variabel seragam yang diperlukan untuk satu lampu. Namun, ketika jumlah lampu berubah, terutama saat menambahkan, Anda perlu menjahit kembali dan mengkompilasi dan menautkan shader. Pada saat ini, Anda juga perlu mengatur materi.
Ubah teksturPerubahan tekstur di sini tidak berarti memperbarui data tekstur, melainkan bahwa bahan asli menggunakan tekstur, tetapi tidak digunakan nanti, atau materi asli tidak menggunakan tekstur, dan kemudian menambahkannya. Jika materi tidak diperbarui secara manual, efek akhir akan berbeda dari apa yang Anda pikirkan. Alasan untuk masalah ini mirip dengan pencahayaan yang disebutkan di atas, dan itu juga karena makro ditambahkan ke shader untuk menentukan apakah tekstur digunakan.
parameter.map? "#define use_map": "",
parameter.envmap? "#define use_envmap": "",
parameter.lightmap? "#define use_lightmap": "",
parameter.bumpmap? "#define use_bumpmap": "",
parameter.normalmap? "#define use_normalmap": "",
parameter.specularmap? "#define use_specularmap": "",
Oleh karena itu, setiap kali peta, envmap atau lightmap mengubah nilai sebenarnya, Anda perlu memperbarui materi
Perubahan dalam data simpul lainnyaBahkan, perubahan tekstur di atas akan menciptakan masalah. Ini terutama karena tidak ada tekstur selama inisialisasi. Namun, dalam lingkungan ini ditambahkan secara dinamis, tidak cukup untuk mengatur materi. Ini juga perlu mengatur geometri.uvsNeedSupdate ke True. Mengapa ada masalah seperti itu? Itu karena optimalisasi program oleh Three.js. Ketika menginisialisasi geometri dan materi untuk pertama kalinya di renderer, jika dinilai bahwa tidak ada tekstur, meskipun ada masing -masing data UV simpul dalam data dalam memori, tiga.js tidak akan menyalin data ini ke dalam memori video. Niat asli adalah menyimpan beberapa ruang memori video yang berharga. Namun, setelah menambahkan tekstur, geometri tidak akan secara cerdas mentransfer data UV ini untuk penggunaan tekstur. Kita harus secara manual mengatur UVSNeedSupdate untuk menginformasikan bahwa sekarang saatnya untuk memperbarui UV, pertanyaan ini benar -benar membuat saya curang untuk waktu yang lama di awal.
Untuk atribut kebutuhan dari beberapa jenis data vertex, Anda dapat melihat masalah ini
https://github.com/mrdoob/three.js/wiki/updates
akhirnyaOptimalisasi tiga.js melakukan pekerjaan dengan baik, tetapi membawa berbagai jebakan yang mungkin disentuh oleh berbagai optimisasi. Cara terbaik untuk melakukan ini adalah dengan melihat kode sumber, atau pergi ke GitHub untuk menyebutkan masalah