Verwenden der vollen Auflösung des Tiefenpuffers für das 2D-Rendering

9

Ich arbeite an einem Front-to-Back-Renderer für eine 2D-Engine mit einer orthografischen Projektion. Ich möchte den Tiefenpuffer verwenden, um ein Überziehen zu vermeiden. Ich habe einen 16-Bit-Tiefenpuffer, eine Kamera bei Z = 100 mit Blick auf Z = 0, zNear ist 1 und zFar ist 1000. Jedes gerenderte Sprite setzt seine Z-Koordinaten auf immer weiter entfernte Werte, sodass der Tiefentest das Rendern überspringen kann alles was darunter ist.

Mir ist jedoch bewusst, dass die Art und Weise, wie Z-Positionen mit Z-Pufferwerten enden, nicht linear ist. Ich möchte die volle Auflösung des 16-Bit-Tiefenpuffers nutzen, dh 65536 eindeutige Werte zulassen. Daher möchte ich für jedes gerenderte Sprite die Z-Position auf die nächste Position erhöhen, um sie mit dem nächsten eindeutigen Tiefenpufferwert zu korrelieren.

Mit anderen Worten, ich möchte einen inkrementierenden Index (0, 1, 2, 3 ...) des eingezogenen Sprites an die entsprechende Z-Position für jedes Sprite drehen, um einen eindeutigen Tiefenpufferwert zu erhalten. Ich bin mir nicht sicher, welche Mathematik dahinter steckt. Was ist die Berechnung, um dies zu tun?

Hinweis: Ich arbeite in WebGL (im Grunde OpenGL ES 2) und muss eine breite Palette von Hardware unterstützen. Erweiterungen wie gl_FragDepth erleichtern dies möglicherweise, können es jedoch aus Kompatibilitätsgründen nicht verwenden.

AshleysBrain
quelle
Ich kann mir nicht vorstellen, dass die Verwendung des Z-Puffers Ihnen einen erheblichen Leistungsgewinn (falls vorhanden) bietet, nachdem Sie alle Z-Puffer-Schreibvorgänge, -Berechnungen und -Vergleiche hinzugefügt haben, anstatt Texturen von hinten nach vorne zu kopieren, ganz zu schweigen von Alpha-Transparenz / Überblendung Leiden.
Matt Esch
@MattEsch: Die Idee ist, dass all diese Berechnungen in der GPU mit unglaublich hohen Geschwindigkeiten durchgeführt werden, also ist es sinnvoll, dies zu tun.
Panda Pyjama
@MattEsch: FWIW Dies richtet sich an integrierte Intel-GPUs, die Systemspeicher anstelle von dediziertem GPU-Speicher verwenden. Dies macht sie ziemlich langsam und neigt dazu, Füllratengrenzen zu erreichen, wenn viele Sprites überzeichnet werden. Intel hat mir diesen Ansatz empfohlen, um ihn zu umgehen. Vermutlich ist die Implementierung von Tiefenprüfungen gut optimiert und kann viel Füllrate einsparen. Es bleibt abzuwarten, ich habe es noch nicht profiliert!
AshleysBrain
@PandaPajama Block Kopieren von Speicher ist eigentlich sehr schnell. Wenn Sie also nur Texturen auf eine Oberfläche blitzen, wäre dies in der Tat sehr schnell. Der erste große Aufwand besteht darin, Daten auf die GPU zu übertragen, was, wie Ashley betont, bei integrierten GPUs teurer sein kann. Sie stellen fest, dass selbst viele 3D-Spiele (wie die Knochenanimation) nicht trivial viel Arbeit auf der CPU leisten, da das Hochladen der Daten, die für diese Matrixberechnungen erforderlich sind, zu teuer ist.
Matt Esch
@MattEsch: Es gibt nur so viel, was man nur mit Blitting machen kann. Rotationen, Skalierungen und Verformungen kommen in den Sinn, aber auch da Sie Pixel- / Vertex-Shader haben, ist die Grenze dessen, was Sie mit Hardware tun können, viel höher als das, was Sie mit bloßem Blitting tun können.
Panda Pyjama

Antworten:

5

In der Tat sind die im Z-Puffer gespeicherten Werte nicht linear zu den tatsächlichen Z-Koordinaten Ihrer Objekte, sondern zu deren Kehrwert, um dem, was sich in der Nähe des Auges befindet, eine höhere Auflösung zu verleihen als dem, was sich näher an der Rückwand befindet.

Was Sie tun, ist, dass Sie Ihr zNearzu 0und Ihr zFarzu zuordnen 1. Für zNear=1und zFar=2sollte es so aussehen

Zbuffer

Die Art und Weise, dies zu berechnen, wird definiert durch:

z_buffer_value = k * (a + (b / z))

Wo

 k = (1 << N), maximum value the Z buffer can store
 N = number of bits of Z precision
 a = zFar / ( zFar - zNear )
 b = zFar * zNear / ( zNear - zFar )
 z = distance from the eye to the object

... und z_buffer_value ist eine ganze Zahl.

Die obige Gleichung wurde Ihnen mit freundlicher Genehmigung dieser fantastischen Seite zur Verfügung gestellt , auf der Z-Puffer auf wirklich gute Weise erklärt werden.

Um das Notwendige zfür eine bestimmte zu finden z_buffer_value, löschen wir einfach Folgendes z:

z = (k * b) / (z_buffer_value - (k * a))
Panda Pyjama
quelle
Danke für die Antwort! Ich bin ein wenig verwirrt, wie Sie zu Ihrer endgültigen Formel gekommen sind. Wenn ich nehme z_buffer_value = k * (a + (b / z))und einfach neu ordne, um zu lösen z, dann bekomme ich: z = b / ((z_buffer_value / k) - a)- wie bist du zu der anderen letzten Formel gekommen?
AshleysBrain
@AshleysBrain: Du nimmst den Nenner (v / k) - a => (v - k * a) / kund kollabierst zu (k * b) / (v - (k * a)). Es ist das gleiche Ergebnis.
Panda Pyjama
Ah ich sehe. Danke für die Antwort, es funktioniert gut!
AshleysBrain
0

Vielleicht sollten Sie Ihre Herangehensweise an etwas Einfacheres ändern. Was ich tun würde; Behalten Sie Ihre Z-Tiefen-Sache bei, aber führen Sie eine Liste dessen, was Sie rendern. Ordnen Sie diese Liste basierend auf dem Wert z Depth und rendern Sie Objekte in der Reihenfolge der Liste.

Hoffe das kann helfen. Die Leute sagen mir immer, ich soll die Dinge einfach halten.

HgMerk
quelle
1
Entschuldigung, das ist nicht viel Hilfe. Das mache ich schon Die Frage ist, welche Z-Positionen zu wählen sind.
AshleysBrain
0

Müssen Sie den Z-Index wirklich erhöhen, da Sie bereits eine sortierte Liste der zu rendernden Dinge (von vorne nach hinten) haben? Können Sie nicht "kleiner oder gleich" für die "Überprüfungsfunktion" verwenden? Auf diese Weise wird tatsächlich geprüft, ob ein bestimmtes Pixel bereits gezeichnet wurde oder nicht.

Elf
quelle
"kleiner oder gleich" führt immer noch dazu, dass absolut alles überzeichnet wird, da alles immer einen gleichen Z-Index hat und daher den Tiefentest besteht.
AshleysBrain