Atribut OpenGL Vertex - Normalisasi

8

Sayangnya, saya telah mencari, dan tidak menemukan jawaban yang pasti.

Kapan Anda akan menormalkan data vertex di OpenGL menggunakan perintah berikut:

glVertexAttribPointer(index, size, type, normalize, stride, pointer);

Yaitu kapan akan normalize == GL_TRUE; situasi apa, dan mengapa Anda memilih untuk membiarkan GPU melakukan perhitungan alih-alih memprosesnya? Semua contoh yang pernah saya lihat, set ini ke GL_FALSE; dan saya pribadi tidak bisa melihat gunanya. Tapi Khronos tidak bodoh, jadi itu pasti ada untuk sesuatu yang berguna (dan mungkin umum).

kaviar melambat
sumber

Jawaban:

9

Parameter "dinormalisasi" mempengaruhi penggunaan nilai titik tetap.

Dokumen ini mengatakan parameter "Menentukan apakah nilai data titik tetap harus dinormalisasi (GL_TRUE) atau dikonversi secara langsung sebagai nilai titik tetap (GL_FALSE) ketika mereka diakses."

Lebih jauh, ini menyatakan "Untuk glVertexAttribPointer, jika dinormalisasi diatur ke GL_TRUE, ini menunjukkan bahwa nilai yang disimpan dalam format integer harus dipetakan ke kisaran [-1,1] (untuk nilai yang ditandatangani) atau [0,1] ( untuk nilai yang tidak ditandatangani) ketika mereka diakses dan dikonversi ke floating point. Jika tidak, nilai akan dikonversi menjadi mengapung secara langsung tanpa normalisasi. "

Singkatnya, jika Anda tidak menggunakan nilai titik tetap, Anda tidak perlu peduli. Jika ya, flag ini mengontrol apakah (misalnya) nilai byte 128 harus 0,5 atau 128,0.

Jari Komppa
sumber
3
Ini tidak benar-benar menjawab pertanyaan, saya sudah mengerti apa yang dilakukannya, tetapi saya ingin tahu kapan itu akan benar-benar berlaku untuk implementasi 'umum'.
deceleratedcaviar
2
Saya cukup yakin itu relevan kembali ketika banyak aplikasi mobile yang menggunakan matematika titik tetap terutama dan tidak ingin menyentuh matematika titik mengambang. Kemungkinan lain adalah bahwa kadang-kadang Anda mungkin ingin menyimpan data dengan cara yang lebih kompak, katakanlah, dalam byte, jika ketepatannya cukup, dan biarkan OpenGL mengubah byte menjadi rentang 0..1.
Jari Komppa
Itu masih relevan. Pikirkan coords UV misalnya. Anda mungkin tidak ingin menyia-nyiakan ruang dan menyimpannya sebagai float2, jadi Anda menyimpannya sebagai 2 uint8 sebagai pengoptimalan dan mengatur bendera normalisasi sehingga Anda akan mendapatkan rentang [0, 1] alih-alih [0, 256] di program.
batal
Apakah 128 akan menjadi 0,5 atau 128/255 = sedikit lebih tinggi?
riv
11

Ini adalah pertanyaan lama, tetapi jawaban saat ini tidak benar-benar menjelaskan untuk apa Anda akan menggunakannya.

Ini semua tentang menghemat ruang. Dan dengan atribut vertex, lebih sedikit ruang dapat berarti kinerja lebih tinggi (jika Anda terikat transfer vertex).

Warna biasanya tidak membutuhkan lebih dari 8-bit per komponen. Kadang-kadang Anda membutuhkan 16-bit, jika itu nilai cahaya HDR atau sesuatu. Tetapi untuk karakteristik permukaan (yang merupakan atribut vertex paling banyak), 8 bit baik-baik saja. Jadi byte dinormalisasi unsigned adalah format simpul yang baik.

Koordinat tekstur tidak perlu presisi floating-point 32-bit. Nilai 16-bit dari [0, 1] sudah cukup. Jadi celana pendek unsigned yang dinormalisasi adalah format vertex yang masuk akal.

Normalnya tidak pernah membutuhkan presisi 32-bit. Mereka arah. 8-bit normalized byte yang ditandatangani cenderung agak kecil, tetapi nilai normalisasi 10-bit cukup baik. OpenGL (3.3+) bahkan memungkinkan Anda untuk menggunakan normals 10-bit melalui format 10/10/10/2 bit, disimpan dalam integer 32-bit unsigned tunggal.

Anda bahkan dapat bermain game dengan posisi titik, jika Anda membutuhkan lebih banyak memori.

Tanpa normalisasi, Anda harus membuang siklus berharga dalam atribut byte pembagi shader Anda dengan 255.0. Mengapa melakukan ini, ketika perangkat keras dapat melakukannya secara gratis?

Nicol Bolas
sumber
Jawaban yang bagus. Tetapi apakah overhead dari divisi floating point matematika sebanding dengan penurunan ukuran normal 50%? (Yaitu 32bit untuk mengatakan 16bit); pada perangkat keras MODERN. Terutama karena, kita tidak benar-benar perlu khawatir tentang terlalu banyak memori (setidaknya dalam kasus saya).
deceleratedcaviar
1
@Aniel: Baiklah, mari kita pikirkan. Pada "perangkat keras MODERN", overhead untuk melewatkan nilai yang dinormalkan adalah ... nol. Overhead melakukan pembagian dalam shader adalah ... tidak nol. Tidak peduli seberapa kecilnya itu, itu masih lebih besar dari nol. Belum lagi penghematan bandwidth mengirimkan blok data vertex 32-byte daripada blok 64-byte. Oh, dan shader tidak harus diprogram secara khusus; Anda bisa menggunakan shader yang sama untuk mengambil nilai yang dinormalisasi atau mengambang. Jika Anda meletakkan divisi di shader, sekarang Anda membutuhkan shader baru untuk ukuran data vertex yang berbeda.
Nicol Bolas
Terima kasih atas jawaban yang bagus. Saya pasti akan mengingatnya, itu tidak perlu saat ini ketika saya mengirim data vertex yang disejajarkan, tetapi ketika saya menambahkan lebih banyak informasi itu mungkin tidak sama, dan ini mungkin merupakan manfaat besar, baik dalam caching dan throughput.
deceleratedcaviar
Adakah strategi tentang cara mengubah normal (disimpan saat ini dalam urutan (3x) floats 32 bit) ke int 32 bit (10/10/10/2)?
deceleratedcaviar
@Aniel: Jika Anda bertanya bagaimana menggunakan ekstensi ARB_vertex_type_2_10_10_10_rev , maka saya sarankan Anda membaca spesifikasi ekstensi yang baru saja saya tautkan . Jika Anda bertanya bagaimana secara pribadi melakukan manipulasi bit, saya akan menyarankan struct C-style yang berisi bidang bit .
Nicol Bolas
0

Bayangkan Anda memiliki mesh dengan normals presisi tinggi dan mesh lain dengan low. Dengan normalisasi atribut, Anda dapat menggunakan shader yang sama tetapi mengemas normals mesh kedua dalam byte.

Stepan Zastupov
sumber