Saya harus menulis rasterizer 3d perangkat lunak saya sendiri, dan sejauh ini saya dapat memproyeksikan model 3d saya yang terbuat dari segitiga ke dalam ruang 2d:
Saya memutar, menerjemahkan, dan memproyeksikan poin saya untuk mendapatkan representasi ruang 2d dari setiap segitiga. Kemudian, saya mengambil 3 titik segitiga dan saya menerapkan algoritma scanline (menggunakan interpolasi linier) untuk menemukan semua titik [x] [y] di sepanjang tepi (kiri dan kanan) dari segitiga, sehingga saya dapat memindai segitiga secara horizontal, baris demi baris, dan isi dengan piksel.
Ini bekerja. Kecuali saya juga harus menerapkan z-buffering. Ini berarti bahwa dengan mengetahui koordinat z yang diputar & diterjemahkan dari 3 simpul segitiga, saya harus menginterpolasi koordinat z untuk semua titik lain yang saya temukan dengan algoritma garis pemindaian saya.
Konsepnya tampak cukup jelas, saya pertama kali menemukan Za dan Zb dengan perhitungan berikut:
var Z_Slope = (bottom_point_z - top_point_z) / (bottom_point_y - top_point_y);
var Za = top_point_z + ((current_point_y - top_point_y) * Z_Slope);
Kemudian untuk setiap Zp saya melakukan interpolasi yang sama secara horizontal:
var Z_Slope = (right_z - left_z) / (right_x - left_x);
var Zp = left_z + ((current_point_x - left_x) * Z_Slope);
Dan jika z saat ini lebih dekat ke penampil daripada z sebelumnya pada indeks itu MAKA tulis warna ke buffer warna DAN tulis z baru ke buffer z. (sistem koordinat saya adalah x: kiri -> kanan; y: atas -> bawah; z: wajah Anda -> layar komputer;)
Masalahnya adalah, rusak. Proyeknya ada di sini dan jika Anda memilih tombol radio "Z-Buffered", Anda akan melihat hasilnya ... ( perhatikan bahwa saya menggunakan algoritma pelukis (-hanya- untuk menggambar bingkai gambar) dalam mode "Z-Buffered" untuk keperluan debugging )
PS: Saya sudah baca di sini bahwa Anda harus mengubah z menjadi timbal balik mereka (artinya z = 1/z
) sebelum Anda melakukan interpolasi. Saya mencobanya, dan tampaknya tidak ada perubahan. Apa yang saya lewatkan? (adakah yang bisa menjelaskan, tepatnya di mana Anda harus mengubah z menjadi 1 / z dan di mana (jika) untuk mengembalikannya?)
[EDIT] Berikut beberapa data tentang nilai z maksimum dan minimum yang saya dapatkan:
max z: 1; min z: -1; //<-- obvious, original z of the vertices of the triangles
max z: 7.197753398761272; min z: 3.791703256899924; //<-- z of the points that were drawn to screen (you know, after rotation, translation), by the scanline with zbuffer, gotten with interpolation but not 1/z.
max z: 0.2649908532179404; min z: 0.13849507306889008;//<-- same as above except I interpolated 1/z instead of z.
//yes, I am aware that changing z to 1/z means flipping the comparison in the zBuffer check. otherwise nothing gets drawn.
Sebelum saya melakukan debugging yang susah payah, dapatkah seseorang mengonfirmasi bahwa konsep saya sejauh ini benar?
[EDIT2]
Saya telah memecahkan z-buffering. Ternyata, urutan gambar tidak kacau sama sekali. Koordinat z dihitung dengan benar.
Masalahnya adalah, dalam upaya meningkatkan frame rate saya, saya menggambar kotak 4px / 4px, setiap piksel ke-4, bukan piksel aktual di layar. Jadi saya menggambar 16px per piksel, tetapi memeriksa z buffer hanya untuk satu dari mereka. Aku payah.
TL / DR: Pertanyaannya masih tetap: Bagaimana / mengapa / kapan Anda harus menggunakan kebalikan dari Z (seperti pada 1 / z) alih-alih Z? Karena saat ini, semuanya berjalan baik. (tidak ada perbedaan nyata).
sumber
Jawaban:
Jawaban cepat: Z bukan fungsi linear dari (X ', Y'), tetapi 1 / Z adalah. Karena Anda menginterpolasi secara linear, Anda mendapatkan hasil yang benar untuk 1 / Z, tetapi tidak untuk Z.
Anda tidak memperhatikan karena selama perbandingan antara Z1 dan Z2 benar, zbuffer akan melakukan hal yang benar, bahkan jika kedua nilai tersebut salah. Anda pasti akan melihat ketika Anda menambahkan pemetaan tekstur (dan untuk menjawab pertanyaan Anda akan memiliki: interpolasi 1 / Z, U / Z dan V / Z, dan merekonstruksi U dan V dari nilai-nilai ini: U = (U / Z) / (1 / Z), V = (V / Z) / (1 / Z). Anda akan berterima kasih kepada saya nanti)
Sebuah contoh. Dapatkan selembar kertas. Tampilan top-down, jadi lupakan koordinat Y. X adalah sumbu horizontal, Z adalah sumbu vertikal, kamera berada pada (0, 0), bidang proyeksi adalah z = 1.
Pertimbangkan poin A (-2, 2) dan B (2, 4). Titik tengah M dari segmen AB adalah (0, 3). Sejauh ini baik.
Anda memproyeksikan A ke A ': X' = X / Z = -1, jadi A 'adalah (-1, 1). Demikian juga, B 'adalah (0,5, 1). Tetapi perhatikan bahwa proyeksi M adalah (0, 1), yang BUKAN titik tengah A'B '. Mengapa? Karena bagian kanan segmen lebih jauh dari kamera daripada bagian kiri, sehingga terlihat lebih kecil.
Jadi apa yang terjadi jika Anda mencoba untuk menghitung ZM menggunakan interpolasi linier? dx = (0,5 - -1) = 1,5, dz = (4 - 2) = 2, jadi untuk M 'di mana X' = 0, Z yang diinterpolasi secara linier adalah zA + (dz / dx) (x - xA) = 2 + (2 / 1.5) (0 - -1) = 2 + 1.333 = 3.3333 - BUKAN 3!
Mengapa? Karena untuk setiap langkah dalam arah X ', Anda tidak memindahkan jumlah yang sama dalam arah Z (atau, dengan kata lain, Z bukan fungsi linier X'). Mengapa? Karena semakin Anda berbelok ke kanan, semakin jauh ruasnya dari kamera, jadi satu piksel mewakili jarak yang lebih jauh di ruang angkasa.
Akhirnya, apa yang terjadi jika Anda menginterpolasi 1 / Z sebagai gantinya? Pertama, Anda menghitung 1 / Z pada A dan B: 0,5 dan 0,25 masing-masing. Kemudian Anda menginterpolasi: dx = (0,5 - -1) = 1,5, dz = (0,25 - 0,5) = -0,25, jadi pada X '= 0 Anda menghitung 1 / Z = 0,5 + (-0,25 / 1,5) * (0 - -1) = 0,3333. Tapi itu 1 / Z, jadi nilai Z adalah ... tepat, 3. Seperti seharusnya.
Ya, matematika itu luar biasa.
sumber