Muszę napisać własny programowy rasterizer 3d i do tej pory jestem w stanie rzutować mój model 3d wykonany z trójkątów na przestrzeń 2d:
Obracam, tłumaczę i rzutuję moje punkty, aby uzyskać przestrzenną reprezentację każdego trójkąta. Następnie biorę 3 punkty trójkąta i implementuję algorytm skanowania (używając interpolacji liniowej), aby znaleźć wszystkie punkty [x] [y] wzdłuż krawędzi (lewej i prawej) trójkątów, aby móc skanować trójkąt w poziomie, rząd po rzędzie i wypełnij go pikselami.
To działa. Tyle, że muszę również zaimplementować buforowanie Z. Oznacza to, że znając obrócone i przetłumaczone współrzędne z 3 wierzchołków trójkąta, muszę interpolować współrzędną z dla wszystkich innych punktów, które znajduję za pomocą mojego algorytmu skanowania.
Koncepcja wydaje się dość jasna, po raz pierwszy znajduję Za i Zb z tymi obliczeniami:
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);
Następnie dla każdej Zp wykonuję tę samą interpolację w poziomie:
var Z_Slope = (right_z - left_z) / (right_x - left_x);
var Zp = left_z + ((current_point_x - left_x) * Z_Slope);
A jeśli bieżący z jest bliżej przeglądarki niż poprzedni z pod tym indeksem, Później zapisz kolor do bufora kolorów ORAZ napisz nowy z do bufora z. (mój układ współrzędnych to x: lewy -> prawy; y: górny -> dolny; z: twoja twarz -> ekran komputera;)
Problem polega na tym, że szaleje. Projekt jest tutaj i jeśli wybrać „Z buforowane” przycisk opcji, zobaczysz wyniki ... ( uwaga, że mogę używać algorytmu malarza (-only- narysować szkielet) w trybie „Z-buforowanej” do celów debugowania )
PS: Przeczytałem tutaj, że z = 1/z
przed interpolacją musisz przekształcić litery Z w ich odwrotność (znaczenie ). Próbowałem tego i wydaje się, że nie ma zmian. czego mi brakuje? (czy ktokolwiek mógłby wyjaśnić, gdzie dokładnie trzeba zmienić z na 1 / z i gdzie (jeśli), aby go odwrócić?)
[EDYCJA] Oto kilka danych o tym, jakie maksymalne i minimalne wartości Z otrzymuję:
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.
Zanim przejdę do starannego debugowania, czy ktoś może potwierdzić, że moja koncepcja do tej pory jest poprawna?
[EDIT2]
Rozwiązałem buforowanie Z. Jak się okazuje, kolejność rysowania wcale nie była pomieszana. Współrzędne Z były obliczane poprawnie.
Problem polegał na tym, że próbując zwiększyć częstotliwość klatek, rysowałem pola 4px / 4px, co 4 piksele, zamiast rzeczywistych pikseli na ekranie. Rysowałem więc 16 pikseli na piksel, ale sprawdzałem bufor Z tylko dla jednego z nich. Jestem takim cyckiem.
TL / DR: Pytanie nadal brzmi: jak / dlaczego / kiedy trzeba używać odwrotności Z (jak w 1 / z) zamiast Z? Ponieważ teraz wszystko działa w obie strony. (nie ma zauważalnej różnicy).
źródło
Odpowiedzi:
Szybka odpowiedź: Z nie jest funkcją liniową (X ', Y'), ale 1 / Z jest. Ponieważ interpolujesz liniowo, otrzymujesz poprawne wyniki dla 1 / Z, ale nie dla Z.
Nie zauważysz, ponieważ dopóki porównanie między Z1 i Z2 jest prawidłowe, zbuffer zrobi właściwą rzecz, nawet jeśli obie wartości są błędne. Na pewno zauważysz, gdy dodasz odwzorowanie tekstur (i aby odpowiedzieć na pytanie, które będziesz mieć: interpoluj 1 / Z, U / Z i V / Z, i zrekonstruuj U i V z tych wartości: U = (U / Z) / (1 / Z), V = (V / Z) / (1 / Z). Podziękujesz mi później)
Przykład. Weź kawałek papieru. Widok z góry, więc zapomnij o współrzędnej Y. X jest osią poziomą, Z jest osią pionową, kamera znajduje się w (0, 0), płaszczyzna projekcji wynosi z = 1.
Rozważ punkty A (-2, 2) i B (2, 4). Punkt środkowy M odcinka AB to (0, 3). Na razie w porządku.
Projektujesz A na A ': X' = X / Z = -1, więc A 'wynosi (-1, 1). Podobnie B 'wynosi (0,5, 1). Zauważ jednak, że rzut M jest (0, 1), który NIE jest punktem środkowym A'B '. Dlaczego? Ponieważ prawa połowa segmentu znajduje się dalej od aparatu niż lewa połowa, więc wygląda na mniejszą.
Co się stanie, jeśli spróbujesz obliczyć Z M 'przy użyciu interpolacji liniowej? dx = (0,5 - -1) = 1,5, dz = (4 - 2) = 2, więc dla M 'gdzie X' = 0 interpolacja liniowa Z to zA + (dz / dx) (x - xA) = 2 + (2 / 1,5) (0 - -1) = 2 + 1,333 = 3,3333 - NIE 3!
Dlaczego? Ponieważ dla każdego kroku w kierunku X 'nie przesuwasz tej samej wartości w kierunku Z (lub innymi słowy, Z nie jest funkcją liniową X'). Dlaczego? Ponieważ im bardziej idziesz w prawo, tym bardziej segment jest oddalony od kamery, więc jeden piksel reprezentuje większą odległość w przestrzeni.
Co się stanie, jeśli interpolujesz 1 / Z zamiast tego? Najpierw obliczasz 1 / Z dla A i B: odpowiednio 0,5 i 0,25. Następnie interpolujesz: dx = (0,5 - -1) = 1,5, dz = (0,25 - 0,5) = -0,25, więc przy X '= 0 obliczasz 1 / Z = 0,5 + (-0,25 / 1,5) * (0 - -1) = 0,3333. Ale to 1 / Z, więc wartość Z wynosi ... dokładnie, 3. Jak powinna być.
Tak, matematyka jest niesamowita.
źródło