Інтерполяція по глибині для z-буфера зі скануванням


10

Мені потрібно написати свій власний програмний 3D-растризатор, і поки що я можу спроектувати свою 3d-модель з трикутників у 2d простір:

Я обертаю, перекладаю і проектую свої точки, щоб отримати 2d-пробільне представлення кожного трикутника. Потім я беру три точки трикутника і реалізую алгоритм сканування (використовуючи лінійну інтерполяцію), щоб знайти всі точки [x] [y] по краях (зліва та справа) трикутників, щоб я міг сканувати трикутник по горизонталі, рядок за рядком і заповнюйте його пікселями.

Це працює. За винятком того, що я також повинен реалізувати z-буферизацію. Це означає, що знаючи обернені та перекладені z координати трьох вершин трикутника, я повинен інтерполювати координату z для всіх інших точок, які я знаходжу за допомогою свого алгоритму сканування.

Концепція здається досить зрозумілою, я спочатку знаходжу Za і Zb з цими розрахунками:

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

Тоді для кожного Zp я роблю однакову інтерполяцію по горизонталі:

var Z_Slope = (right_z - left_z) / (right_x - left_x);
var Zp = left_z + ((current_point_x - left_x) * Z_Slope);

введіть тут опис зображення

І якщо поточний z ближче до глядача, ніж попередній z, в цьому індексі ТАКІ записуйте колір у буфер кольорів І записуйте нове z у буфер z. (моя система координат x: ліворуч -> праворуч; y: верх -> низ; z: ваше обличчя -> екран комп'ютера;)

Проблема полягає в тому, що вона перебуває без перешкод. Проект є тут, і якщо ви виберете перемикач "Z-Buffered", ви побачите результати ... ( зауважте, що я використовую алгоритм художника (-ліше- для малювання каркасної рамки) в режимі "Z-Buffered" для налагодження )

PS: Я прочитав тут, що ви повинні перетворити z у їхні взаємні вимоги (тобто z = 1/z), перш ніж інтерполювати. Я спробував це, і виявляється, що змін немає. Що я пропускаю? (хтось може уточнити, куди потрібно повернути z в 1 / z і де (якщо), щоб повернути його назад?)

[EDIT] Ось деякі дані щодо того, які максимальні та мінімальні значення я отримую:

    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.

Перш ніж я перейду до кропіткої налагодження, може хтось підтвердити, що моя концепція досі правильна?

[EDIT2]

Я вирішив z-буферизацію. Як виявляється, порядок малювання взагалі не був зіпсований. Координати z обчислювались правильно.

Проблема полягала в тому, що, намагаючись збільшити частоту кадрів, я малював 4px / 4px поля, кожен четвертий піксель, а не фактичні пікселі на екрані. Тому я малював 16 пікселів на піксель, але перевіряв буфер z лише для одного з них. Я такий сиськи.

TL / DR: Питання все ще стоїть: як / чому / коли потрібно використовувати зворотні Z (як у 1 / z) замість Z? Тому що зараз все працює в будь-якому випадку. (помітної різниці немає).


Re: "І звичайно я додаю до zBuffer, якщо поточне z ближче до переглядача, ніж попереднє значення в цьому індексі." Мені не зрозуміло, що ви сказали, що, як ви мали намір, але хотіли переконатися, що те, що ви мали на увазі, "якщо поточний z ближче до глядача, ніж попередній z в цьому індексі, ТОТЕ запишіть колір у буфер кольорів і напишіть new z до буфера z "Мета буфера z - блокувати написання кольорів, якщо колір у цьому пікселі вже був записаний ближче до ока камери.
Альтуріс

Це правильно. Вибачте, пізно я сформулював своє запитання. Я перегляну.
Spectraljump

Відповіді:


5

Швидка відповідь: Z не є лінійною функцією (X ', Y'), але 1 / Z є. Оскільки ви інтерполюєте лінійно, ви отримуєте правильні результати для 1 / Z, але не для Z.

Ви не помічаєте, оскільки поки порівняння між Z1 і Z2 правильним, zbuffer зробить правильно, навіть якщо обидва значення неправильні. Ви обов'язково помітите, коли ви додасте відображення текстури (і відповісти на запитання, яке у вас виникне: інтерполювати 1 / Z, U / Z і V / Z, а також реконструювати U і V з цих значень: U = (U / Z) / (1 / Z), V = (V / Z) / (1 / Z). Дякуєш мені згодом)

Приклад. Дістаньте аркуш паперу. Вид зверху вниз, тому забудьте координату Y. X - горизонтальна вісь, Z - вертикальна вісь, камера знаходиться на (0, 0), площина проекції - z = 1.

Розглянемо точки A (-2, 2) і B (2, 4). Середня точка M відрізка AB дорівнює (0, 3). Все йде нормально.

Ви проектуєте A на A ': X' = X / Z = -1, тому A 'є (-1, 1). Так само B 'дорівнює (0,5, 1). Але зауважимо, що проекція M дорівнює (0, 1), що НЕ є серединою A'B '. Чому? Оскільки права половина відрізка розташована далі від камери, ніж ліва половина, тому вона виглядає менше.

Що ж станеться, якщо спробувати обчислити Z M 'за допомогою лінійної інтерполяції? dx = (0,5 - -1) = 1,5, dz = (4 - 2) = 2, тому для M ', де X' = 0, лінійно інтерпольований Z є zA + (dz / dx) (x - xA) = 2 + (2 / 1.5) (0 - -1) = 2 + 1.333 = 3.3333 - НЕ 3!

Чому? Оскільки для кожного кроку у напрямку X 'ви не переміщуєте однакову кількість у напрямку Z (або, іншими словами, Z не є лінійною функцією X'). Чому? Оскільки чим більше ви рухаєтесь направо, тим далі відрізок від камери, тим один піксель являє собою більшу відстань у просторі.

Нарешті, що станеться, якщо замість них інтерполювати 1 / Z? Спочатку ви обчислюєте 1 / Z на A і B: 0,5 і 0,25 відповідно. Тоді ви інтерполюєте: dx = (0,5 - -1) = 1,5, dz = (0,25 - 0,5) = -0,25, тому при X '= 0 ви обчислюєте 1 / Z = 0,5 + (-0,25 / 1,5) * (0 - -1) = 0,3333. Але це 1 / Z, тож значення Z - це ... рівно, 3. Як має бути.

Так, математика є приголомшливою.


1
О, а щодо "коли": обчисліть значення 1 / Z перед тим, як почати растровувати трикутник (наприклад, безпосередньо перед вертикальним циклом), щоб ви отримали інтерполяцію 1 / Z зліва та справа від лінії сканування. Інтерполюйте їх лінійно (НЕ робіть 1 / Z знову - інтерпольовані значення вже 1 / Z!), І скасуйте перетворення безпосередньо перед перевіркою zbuffer.
ggambett

1
І нарешті, чому. Площина (де вбудований трикутник) - Ax + By + Cz + D = 0. z - чітко лінійна функція (x, y). Ви проектуєте так x '= x / z і y' = y / z. Звідти x = x'z і y = y'z. Якщо ви заміните їх у вихідному рівнянні, ви отримаєте Ax'z + By'x + Cz + D = 0. Тепер z = -D / (Ax '+ By' + C), де зрозуміло, що z не є лінійною функцією з (x ', y'). Але 1 / z є, отже, (Ax '+ By' + C) / -D, що є лінійною функцією (x ', y').
ggambett

1
Ви знаєте, я прочитав досить багато статей і курсів, і жодна з них не була такою чіткою, як ваша відповідь. Для нащадків також зазначу, що "Літери" U "і" V "позначають осі 2D текстури, оскільки" X "," Y "і" Z "вже використовуються для позначення осей 3D-об'єкта в моделі простір. УФ-текстурування дозволяє полігони, що складають 3D-об’єкт, бути пофарбованими кольором із зображення. " - Вікіпедія - УФ-карти
Spectraljump

Радий це чути. Насправді я вчив комп'ютерну графіку в попередньому житті :)
ggambett

Велике спасибі - мені завжди було цікаво з цього приводу - і не знаю, чи знайшов би я коли-небудь кращу відповідь! +1
Кодесміт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.