Tiefeninterpolation für Z-Puffer mit Scanline

10

Ich muss meinen eigenen Software-3D-Rasterizer schreiben und kann mein 3D-Modell aus Dreiecken bisher in den 2D-Raum projizieren:

Ich drehe, übersetze und projiziere meine Punkte, um eine 2D-Raumdarstellung jedes Dreiecks zu erhalten. Dann nehme ich die 3 Dreieckspunkte und implementiere den Scanline-Algorithmus (unter Verwendung der linearen Interpolation), um alle Punkte [x] [y] entlang der Kanten (links und rechts) der Dreiecke zu finden, damit ich das Dreieck horizontal scannen kann. Zeile für Zeile und füllen Sie es mit Pixeln.

Das funktioniert. Außer ich muss auch Z-Pufferung implementieren. Dies bedeutet, dass ich bei Kenntnis der gedrehten und übersetzten z-Koordinaten der 3 Eckpunkte des Dreiecks die z-Koordinate für alle anderen Punkte, die ich mit meinem Scanline-Algorithmus finde, interpolieren muss.

Das Konzept scheint klar genug zu sein, ich finde zuerst Za und Zb mit diesen Berechnungen:

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

Dann mache ich für jedes Zp die gleiche Interpolation horizontal:

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

Geben Sie hier die Bildbeschreibung ein

Und wenn das aktuelle z näher am Betrachter liegt als das vorherige z an diesem Index, dann schreiben Sie die Farbe in den Farbpuffer UND schreiben Sie das neue z in den z-Puffer. (Mein Koordinatensystem ist x: links -> rechts; y: oben -> unten; z: dein Gesicht -> Computerbildschirm;)

Das Problem ist, es geht drunter und drüber. Das Projekt ist hier und wenn Sie das Optionsfeld "Z-gepuffert" auswählen, werden die Ergebnisse angezeigt ... ( Beachten Sie, dass ich den Algorithmus des Malers (nur zum Zeichnen des Drahtgitters) im Modus "Z-gepuffert" verwende für Debugging-Zwecke )

PS: Ich habe hier gelesen , dass Sie die Zs in ihre Kehrwerte (Bedeutung z = 1/z) umwandeln müssen, bevor Sie interpolieren. Ich habe das versucht, und es scheint, dass sich nichts ändert. Was vermisse ich? (Könnte jemand klarstellen, wo genau Sie z in 1 / z verwandeln müssen und wo (wenn) Sie es zurückdrehen müssen?)

[EDIT] Hier sind einige Daten darüber, welche maximalen und minimalen z-Werte ich erhalte:

    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.

Kann jemand bestätigen, dass mein bisheriges Konzept korrekt ist, bevor ich mit dem sorgfältigen Debuggen beginne?

[EDIT2]

Ich habe die Z-Pufferung gelöst. Wie sich herausstellte, war die Zeichnungsreihenfolge überhaupt nicht durcheinander. Die z-Koordinaten wurden korrekt berechnet.

Das Problem war, dass ich bei dem Versuch, meine Bildrate zu erhöhen, 4px / 4px-Boxen alle 4 Pixel anstelle der tatsächlichen Pixel auf dem Bildschirm zeichnete. Also habe ich 16 Pixel pro Pixel gezeichnet, aber den Z-Puffer auf nur einen überprüft. Ich bin so ein Busen.

TL / DR: Die Frage bleibt: Wie / warum / wann müssen Sie den Kehrwert von Z (wie in 1 / z) anstelle von Z verwenden? Denn im Moment funktioniert alles so oder so. (Es gibt keinen merklichen Unterschied).

Spektralsprung
quelle
Betreff: "Und natürlich füge ich dem zBuffer hinzu, wenn das aktuelle z näher am Viewer liegt als der vorherige Wert an diesem Index." Mir ist nicht klar, dass Sie das so formuliert haben, wie Sie es wollten, aber sicherstellen wollten, dass das, was Sie meinten, "wenn das aktuelle z näher am Betrachter liegt als das vorherige z an diesem Index, DANN schreiben Sie die Farbe in den Farbpuffer UND schreiben Sie das neues z in den z-Puffer "Der Zweck des z-Puffers besteht darin, Farbschreibvorgänge zu blockieren, wenn eine Farbe an diesem Pixel bereits näher am Kameraauge geschrieben wurde.
Alturis
Das ist richtig. Entschuldigung, es war spät, als ich meine Frage formulierte. Ich werde überarbeiten.
Spectraljump

Antworten:

5

Schnelle Antwort: Z ist keine lineare Funktion von (X ', Y'), sondern 1 / Z. Da Sie linear interpolieren, erhalten Sie korrekte Ergebnisse für 1 / Z, jedoch nicht für Z.

Sie bemerken es nicht, denn solange der Vergleich zwischen Z1 und Z2 korrekt ist, wird der Z-Puffer das Richtige tun, auch wenn beide Werte falsch sind. Sie werden es definitiv bemerken, wenn Sie Textur-Mapping hinzufügen (und um die Frage zu beantworten, die Sie dann haben: Interpolieren Sie 1 / Z, U / Z und V / Z und rekonstruieren Sie U und V aus diesen Werten: U = (U / Z) / (1 / Z), V = (V / Z) / (1 / Z). Du wirst mir später danken.)

Ein Beispiel. Holen Sie sich ein Stück Papier. Ansicht von oben nach unten, vergessen Sie also die Y-Koordinate. X ist die horizontale Achse, Z ist die vertikale Achse, die Kamera ist auf (0, 0), die Projektionsebene ist z = 1.

Betrachten Sie die Punkte A (-2, 2) und B (2, 4). Der Mittelpunkt M des Segments AB ist (0, 3). So weit, ist es gut.

Sie projizieren A in A ': X' = X / Z = -1, also ist A '(-1, 1). Ebenso ist B '(0,5, 1). Beachten Sie jedoch, dass die Projektion von M (0, 1) ist, was NICHT der Mittelpunkt von A'B 'ist. Warum? Da die rechte Hälfte des Segments weiter von der Kamera entfernt ist als die linke Hälfte, sieht es kleiner aus.

Was passiert also, wenn Sie versuchen, das Z von M 'mithilfe linearer Interpolation zu berechnen? dx = (0,5 - -1) = 1,5, dz = (4 - 2) = 2, also ist für M 'mit X' = 0 das linear interpolierte Z zA + (dz / dx) (x - xA) = 2 + (2 / 1,5) (0 - -1) = 2 + 1,333 = 3,3333 - NICHT 3!

Warum? Weil Sie bei jedem Schritt in X'-Richtung nicht den gleichen Betrag in Z-Richtung bewegen (oder mit anderen Worten, Z ist keine lineare Funktion von X '). Warum? Denn je weiter Sie nach rechts gehen, desto weiter ist das Segment von der Kamera entfernt, sodass ein Pixel eine größere räumliche Entfernung darstellt.

Was passiert schließlich, wenn Sie stattdessen 1 / Z interpolieren? Zuerst berechnen Sie 1 / Z bei A und B: 0,5 bzw. 0,25. Dann interpolieren Sie: dx = (0,5 - -1) = 1,5, dz = (0,25 - 0,5) = -0,25, also berechnen Sie bei X '= 0 1 / Z = 0,5 + (-0,25 / 1,5) * (0 - -1) = 0,3333. Aber das ist 1 / Z, also ist der Wert von Z ... genau 3. Wie es sein sollte.

Ja, Mathe ist großartig.

ggambett
quelle
1
Oh, und in Bezug auf "wann": Berechnen Sie die 1 / Z-Werte, bevor Sie mit dem Rasteren des Dreiecks beginnen (z. B. kurz vor der vertikalen Schleife), sodass Sie links und rechts von der Scanlinie 1 / Z interpolieren. Interpolieren Sie diese linear (machen Sie NICHT 1 / Z erneut - die interpolierten Werte sind bereits 1 / Z!) Und machen Sie die Transformation kurz vor dem Überprüfen des Z-Puffers rückgängig.
Ggambett
1
Und schließlich warum. Eine Ebene (in die das Dreieck eingebettet ist) ist Ax + By + Cz + D = 0. z ist eindeutig eine lineare Funktion von (x, y). Sie projizieren also x '= x / z und y' = y / z. Von dort ist x = x'z und y = y'z. Wenn Sie diese in der ursprünglichen Gleichung ersetzen, erhalten Sie Ax'z + By'x + Cz + D = 0. Jetzt ist z = -D / (Ax '+ By' + C), wobei klar ist, dass z keine lineare Funktion ist von (x ', y'). Aber 1 / z ist daher (Ax '+ By' + C) / -D, was eine lineare Funktion von (x ', y') ist.
Ggambett
1
Weißt du, ich habe einige Artikel und Kurse gelesen, und keiner von ihnen war so klar wie deine Antwort. Für die Nachwelt werde ich auch bemerken, dass "Die Buchstaben" U "und" V "die Achsen der 2D-Textur bezeichnen, da" X "," Y "und" Z "bereits verwendet werden, um die Achsen des 3D-Objekts im Modell zu bezeichnen Durch die UV-Texturierung können Polygone, aus denen ein 3D-Objekt besteht, mit Farbe aus einem Bild gemalt werden. " - Wikipedia - UV-Kartierung
Spectraljump
Freut mich das zu hören. Tatsächlich habe ich in einem früheren Leben Computergrafik unterrichtet :)
ggambett
Vielen Dank - ich war schon immer neugierig - und ich weiß nicht, ob ich jemals eine bessere Antwort gefunden hätte! +1
Codesmith