Interpolasi kedalaman untuk z-buffer, dengan scanline

10

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

masukkan deskripsi gambar di sini

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

Spectraljump
sumber
Re: "Dan tentu saja saya menambahkan ke zBuffer, jika z saat ini lebih dekat ke penampil daripada nilai sebelumnya pada indeks itu." Itu tidak jelas bagi saya bahwa Anda mengatakan bahwa bagaimana Anda bermaksud tetapi ingin memastikan bahwa apa yang Anda maksudkan adalah "jika z saat ini lebih dekat ke penampil daripada z sebelumnya pada indeks itu KEMUDIAN menulis warna ke buffer warna DAN menulis z baru ke buffer z "Tujuan buffer z adalah untuk memblokir warna tulis jika warna pada piksel itu sudah ditulis lebih dekat ke mata kamera.
Alturis
Itu betul. Maaf, sudah terlambat ketika saya mengatakan pertanyaan saya. Saya akan merevisinya.
Spectraljump

Jawaban:

5

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.

ggambett
sumber
1
Oh, dan tentang "kapan": hitung nilai 1 / Z sebelum mulai meraster segitiga (misalnya tepat sebelum loop vertikal), sehingga Anda mendapatkan 1 / Z diinterpolasi di kiri dan kanan garis pemindaian. Interpolasi ini secara linear (JANGAN lakukan 1 / Z lagi - nilai yang diinterpolasi sudah 1 / Z!), Dan urungkan transformasi sebelum memeriksa zbuffer.
ggambett
1
Dan akhirnya, mengapa. Sebuah bidang (tempat segitiga disematkan) adalah Ax + By + Cz + D = 0. z jelas merupakan fungsi linear dari (x, y). Anda memproyeksikan jadi x '= x / z dan y' = y / z. Dari sana, x = x'z dan y = y'z. Jika Anda mengganti ini dalam persamaan asli Anda mendapatkan Ax'z + By'x + Cz + D = 0. Sekarang z = -D / (Ax '+ By' + C), di mana sudah jelas bahwa z bukan fungsi linear dari (x ', y'). Tetapi 1 / z adalah (Ax '+ By' + C) / -D, yang merupakan fungsi linear dari (x ', y').
ggambett
1
Anda tahu, saya membaca beberapa artikel dan kursus, dan tidak ada satupun yang sejelas jawaban Anda. Untuk keturunan, saya juga akan mencatat bahwa "Huruf" U "dan" V "menunjukkan sumbu tekstur 2D karena" X "," Y "dan" Z "sudah digunakan untuk menunjukkan sumbu dari objek 3D dalam model ruang. Tekstur UV memungkinkan poligon yang membentuk objek 3D untuk dicat dengan warna dari gambar. " - Wikipedia - Pemetaan UV
Spectraljump
Senang mendengarnya. Saya memang mengajarkan Grafik Komputer dalam kehidupan sebelumnya :)
ggambett
Terima kasih banyak - saya selalu ingin tahu tentang ini - dan saya tidak tahu apakah saya akan pernah menemukan jawaban yang lebih baik! +1
Codesmith