Menggunakan resolusi penuh buffer kedalaman untuk rendering 2D

9

Saya sedang mengerjakan renderer depan-ke-belakang untuk mesin 2D menggunakan proyeksi ortografis. Saya ingin menggunakan buffer kedalaman untuk menghindari penarikan berlebih. Saya memiliki buffer kedalaman 16-bit, kamera pada Z = 100 melihat Z = 0, zNear adalah 1, dan zFar adalah 1000. Setiap sprite yang diberikan mengatur koordinat Z-nya ke nilai yang semakin jauh, yang memungkinkan uji kedalaman untuk melewatkan rendering apa pun yang ada di bawahnya.

Namun saya sadar cara posisi Z berakhir dengan nilai buffer Z adalah non-linear. Saya ingin memanfaatkan resolusi penuh buffer kedalaman 16-bit, yaitu memungkinkan 65536 nilai unik. Jadi untuk setiap sprite yang diberikan, saya ingin menambah posisi Z ke posisi berikutnya untuk berkorelasi dengan nilai buffer kedalaman unik berikutnya.

Dengan kata lain saya ingin mengubah indeks kenaikan (0, 1, 2, 3 ...) dari sprite yang ditarik ke posisi Z yang tepat untuk setiap sprite untuk memiliki nilai buffer kedalaman yang unik. Saya tidak yakin dengan matematika di balik ini. Apa perhitungan untuk melakukan ini?

Catatan Saya bekerja di WebGL (pada dasarnya OpenGL ES 2), dan saya perlu mendukung berbagai perangkat keras, jadi sementara ekstensi seperti gl_FragDepth mungkin membuat ini lebih mudah, saya tidak dapat menggunakannya untuk alasan kompatibilitas.

AshleysBrain
sumber
Saya tidak bisa membayangkan menggunakan z buffer akan menawarkan banyak keuntungan kinerja (jika ada) setelah Anda menambahkan semua penulisan buffer z, perhitungan dan perbandingan vs menyalin tekstur kembali ke depan, belum lagi transparansi alpha / blending kesengsaraan.
Matt Esch
@MattEsch: Idenya adalah semua perhitungan itu dilakukan dalam GPU dengan kecepatan sangat tinggi, jadi masuk akal untuk melakukan itu.
Piyama Panda
@MattEsch: FWIW ini ditujukan untuk GPU terintegrasi Intel, yang menggunakan memori sistem alih-alih memori GPU khusus. Ini membuat mereka sangat lambat dan cenderung untuk mencapai batas laju pengisian jika menarik banyak sprite. Intel merekomendasikan pendekatan ini kepada saya sebagai cara untuk mengatasinya. Agaknya implementasi pengujian kedalaman mereka dioptimalkan dengan baik dan dapat menghemat banyak laju pengisian. Masih harus dilihat, saya belum memprofilkannya!
AshleysBrain
@PandaPajama menyalin memori blok sebenarnya sangat cepat, jadi jika Anda hanya memuntahkan tekstur ke permukaan itu akan sangat cepat. Overhead utama pertama adalah mendapatkan data ke GPU di tempat pertama, yang sebagai Ashley tunjukkan bisa lebih mahal pada GPU terintegrasi. Anda menemukan bahwa bahkan banyak gim 3d yang melakukan pekerjaan non-sepele pada CPU (seperti animasi tulang) karena mengunggah data yang diperlukan untuk melakukan perhitungan matriks di tempat pertama terlalu mahal.
Matt Esch
@MattEsch: Hanya ada begitu banyak yang dapat Anda lakukan hanya dengan blitting. Rotasi, penskalaan, dan deformasi muncul di pikiran, tetapi juga karena Anda memiliki pixel / vertex shaders, batas apa yang dapat Anda lakukan dengan perangkat keras jauh lebih tinggi daripada apa yang dapat Anda lakukan hanya dengan blitting.
Piyama Panda

Jawaban:

5

Memang, nilai-nilai yang disimpan dalam buffer-z tidak linier dengan koordinat z sebenarnya dari objek Anda, tetapi pada kebalikannya, untuk memberikan lebih banyak resolusi pada apa yang dekat mata daripada pada apa yang lebih dekat dengan bidang belakang.

Yang Anda lakukan adalah memetakan zNearke 0dan zFarke 1. Untuk zNear=1dan zFar=2, seharusnya terlihat seperti ini

Zbuffer

Cara menghitung ini ditentukan oleh:

z_buffer_value = k * (a + (b / z))

Dimana

 k = (1 << N), maximum value the Z buffer can store
 N = number of bits of Z precision
 a = zFar / ( zFar - zNear )
 b = zFar * zNear / ( zNear - zFar )
 z = distance from the eye to the object

... dan z_buffer_value adalah bilangan bulat.

Persamaan di atas diberikan kepada Anda berdasarkan halaman yang luar biasa ini , yang menjelaskan z-buffer dengan cara yang sangat baik.

Jadi, untuk menemukan yang diperlukan zuntuk diberikan z_buffer_value, kami hanya menghapus z:

z = (k * b) / (z_buffer_value - (k * a))
Piyama Panda
sumber
Terima kasih atas jawabannya! Saya agak bingung bagaimana Anda mendapatkan formula akhir Anda. Jika saya mengambil z_buffer_value = k * (a + (b / z))dan mengatur ulang untuk menyelesaikannya z, maka saya mendapatkan: z = b / ((z_buffer_value / k) - a)- bagaimana Anda sampai pada formula terakhir yang berbeda?
AshleysBrain
@AshleysBrain: Anda mengambil penyebut (v / k) - a => (v - k * a) / k, dan runtuh ke (k * b) / (v - (k * a)). Itu hasil yang sama.
Piyama Panda
Ah, begitu. Terima kasih atas jawabannya, ini bekerja dengan baik!
AshleysBrain
0

Mungkin Anda harus mengubah pendekatan Anda ke sesuatu yang lebih sederhana. Apa yang akan saya lakukan; Jaga hal kedalaman Z Anda, tetapi simpan daftar apa yang Anda render. Pesanlah daftar itu berdasarkan pada nilai Kedalaman z, dan render objek dalam urutan daftar.

Semoga ini bisa membantu. Orang-orang selalu mengatakan kepada saya untuk menjaga hal-hal sederhana.

HgMerk
sumber
1
Maaf, itu tidak banyak membantu. Saya sudah melakukan itu. Pertanyaannya adalah tentang posisi Z apa yang harus dipilih.
AshleysBrain
0

Karena Anda sudah memiliki daftar hal-hal yang harus diurutkan (depan ke belakang) apakah Anda benar-benar perlu meningkatkan indeks Z? Tidak bisakah Anda menggunakan "kurang atau sama" untuk "fungsi memeriksa"? Dengan cara ini ia akan benar-benar memeriksa apakah piksel tertentu sudah ditarik atau tidak.

Elven
sumber
"kurang atau sama" masih akan menyebabkan semuanya terlalu berlebihan, karena semuanya akan selalu memiliki indeks Z yang sama dan karenanya lulus uji kedalaman.
AshleysBrain