Tiefentest: Unterschied zwischen den Versionen

Aus DGL Wiki
Wechseln zu: Navigation, Suche
(Hinweis auf glDepthMask)
 
(12 dazwischenliegende Versionen von 8 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
Wenn im letzten Schritt der Grafikpipeline die 3D-Szene in den Framebuffer gerendert wird, muss OpenGL feststellen können, welche Teile der Grafik im Vordergrund stehen und welche Objekte ganz oder teilweise durch andere Objekte verdeckt werden. Zu diesem Zweck wird ein Tiefenpuffer angelegt, der genauso gross wie der Framebuffer ist. Für jeden Pixel auf dem Bildschirm gibt es dann also einen weiteren Wert - den Eintrag im Tiefenpuffer.
+
Bei der Berechnung eines [[Fragment]]es, muss OpenGL feststellen können, welche Teile der Grafik im Vordergrund stehen und welche Objekte ganz oder teilweise durch andere Objekte verdeckt werden.
Im ersten Schritt wird der gesamte Tiefenpuffer auf den größtmöglichen Wert initialisiert (also die maximale Distanz).
 
Sobald dann ein Objekt gerendert wird, wird für jeden Pixel überprüft, ob der Tiefenwert des Pixels dieses Objekts kleiner ist als der Wert im Tiefenpuffer. Ist dies der Fall, wird dieser Pixel in den Framebuffer gezeichnet, ansonsten wird er verworfen. Der Test kann mittels [[glDepthFunc]] auch abgeändert werden. Zum Beispiel kann man dafür sorgen, dass auch gleich große Tiefenwerte akzeptiert werden.
 
  
Da der Tiefentest für jedes Fragment (auch für die die nicht dargestellt werden) durchgeführt werden muss, ist der Tiefentest zwar eine exakte (auf Pixel basis) und einfache, jedoch inperformante Methode um zu verhindern, dass Objekte die hinter anderen Objekten stehen dennoch angezeigt werden. Jedoch muss bei dieser Methode (im Gegensatz zu den Alternativen) keine Tiefensortierung der 3ecke erfolgen. Es spielt bei der Verwendung des Tiefentests also keine Rolle, in welcher Reihenfolge die Objekte an die 3D-Pipeline übergeben werden.
+
== Funktionsweise ==
 +
Für den Tiefentest existiert der [[Tiefenpuffer]](ZBuffer). Dieser ist genauso gross wie der Framebuffer. Für jeden Pixel auf dem Bildschirm gibt es dann also einen weiteren Wert - den Eintrag im Tiefenpuffer.  
  
Hinweis: Sobald Transparenz verwendet wird, kann der Tiefentest zumindest für diese Teile nicht mehr benutzt werden. Dies würde zu Fehlern führen, da das transparente Fragment ggfs. das durch die transparenz eigentlich durchscheinende, jedoch erst später an die Grafik Pipeline gesendete und somit vom Tiefentest verworfene Fragment dahinter völlig verbirgt. Wenn man Transparente Fragmente nicht in den Tiefenbuffer schreibt, so können diese Fragmente von anderen, eigentlich weiter hinten liegenden, Fragmenten komplett überschrieben werden.<br>
+
Im ersten Schritt wird der gesamte Tiefenpuffer auf den größtmöglichen Wert initialisiert (also die maximale Distanz, siehe [[glClear]] und [[glClearDepth]]).
Aus diesem Grund sollten Transparente Objekte nach allen anderen Objekten an OpenGL geschickt werden und diese möglichst von hinten nach vorne sortiert sein. Das Sortieren kann durch einen Alpha Wert im [[FrameBuffer]] umgangen werden, jedoch ist dieser nur selten in Hardware vorhanden, wodurch in den weit langsameren Softwaremodus gewechselt wird.
+
Sobald dann ein Objekt gerendert wird, wird für jeden Pixel überprüft, ob der Tiefenwert des Pixels dieses Objekts kleiner ist als der Wert im Tiefenpuffer. Ist dies der Fall, wird dieses Pixel in den Framebuffer gezeichnet und der Tiefenpuffer aktualisiert, ansonsten wird es verworfen. Der Test kann mittels [[glDepthFunc]] auch abgeändert werden. Zum Beispiel kann man dafür sorgen, dass auch gleich große Tiefenwerte akzeptiert werden.
 +
 
 +
Der Tiefentest findet verhältnismäßig früh in der [[Feste_Funktionspipeline#Fragment_Pipeline|Fragment-Pipeline]] statt, wodurch er sehr effizient nicht benötigte Fragmente ausschließen kann. Wenn Objekte tiefensortiert (in diesem Fall von vorne nach hinten) gezeichnet werden, ist der Tiefentest sehr gut geeignet um sehr früh nicht benötigte Fragmente zu verwerfen um so die [[Füllrate]] zu schonen.
 +
 
 +
Den Tiefentest aktiviert man mit [[glEnable#GL_DEPTH_TEST|glEnable(GL_DEPTH_TEST)]] und deaktiviert ihn entsprechend mit glDisable.
 +
 
 +
Außerdem kann man mit [[glDepthMask]] einstellen, ob beim Rendern des momentanen Objektes der Tiefenbuffer beschrieben werden soll oder nicht.
 +
 
 +
===Aufteilung des Tiefenpuffers===
 +
Was steht im Tiefenpuffer? Im Tiefenpuffer steht nicht die Tiefe sondern die Tiefenzone. Beim erstellen der [[Renderkontext]]s wird angegeben, wie viele Bits für den ZBuffer zur Verfügung stehen. Bei 1Bit, gibt es genau 2^1=2 Tiefenzonen. Bei 4Bit 2^4=16 und bei 24Bit 2^24=16,7Mio Tiefenzonen. Der Bereich zwischen der Near- und Far-[[Clipping]]plane wird nun auf diese Zonen aufgeteilt. Fallen zwei Fragmente in die selbe Zone, entscheidet das durch [[glDepthFunc]] eingestellte Verhalten welches Fragment den Platz behalten/einnehmen darf.
 +
 
 +
'''Ein simples Beispiel'''<br>
 +
{{Hinweis|Die Werte wurden in diesem Beispiel so gewählt, dass ganze Zahlen herauskommen. Das ist in der Realität sehr unwahrscheinlich.}}
 +
ZNear liegt bei 1, ZFar liegt bei 257. Das heißt der Renderbereich ist 256 Einheiten groß.
 +
Dem ZBuffer wurden nur 4Bit gegönnt (eine sehr niedrige Auflösung). Das heißt 16 Zonen.
 +
Jede Tiefenzone ist (bei linearer Aufteilung) 256/16=16 OpenGL Einheiten tief.
 +
 
 +
Wird nun ein Fragment erzeugt, welches den Abstand z=17 zur Kamera hat (also Zone 2 von vorn) und ein anderes Fragment den Abstand z=35 hat (Zone 3) ist klar, welches Fragment näher ist.
 +
Nehmen wir an es kommt ein drittes Fragment hinzu mit dem Abstand 20. Für uns ist klar, dass dieses hinter dem ersten Fragment gehört. Allerdings ist der ZBuffer nicht fein genug aufgelöst, um das genauso zu sehen. Für ihn fällt auch dieses Fragment in Zone 2 und unter Umständen entscheidet die glDepthFunc Einstellung nun sogar, dass das neue Fragment das alte überschreiben darf. D.h. das 3. Fragment ist sichtbar obwohl es verdeckt sein sollte!
 +
 
 +
Zu beachten ist, dass nicht Bitanzahl des ZBuffers allein, die Auflösung bestimmt. Auch die Distanz zwischen den beiden Clippingplanes spielt eine Rolle. Je größer diese wird, desto grober wird der ZBuffer aufgelöst.
 +
 
 +
 
 +
=== Beispiel ===
 +
 
 +
[[Bild:Depthtest_nt_enabled.jpg|thumb|200px|Aktivierter Tiefentest]]
 +
[[Bild:Depthtest_nt_disabled.jpg|thumb|200px|Deaktivierter Tiefentest]]
 +
Dieser Rendercode wird einmal mit und einmal ohne Tiefentest verwendet.
 +
  glBegin(GL_QUADS);
 +
    glColor4f(1.0, 0.5, 0.5, 1.0);
 +
    glVertex3f(-1.0, 0.0, -1.0);
 +
    glVertex3f(-1.0, 0.0, 1.0);
 +
    glVertex3f(1.0, 0.0, 1.0);
 +
    glVertex3f(1.0, 0.0, -1.0);
 +
 +
    glColor4f(0.5, 1.0, 0.5, 1.0);
 +
    glVertex3f(-1.0, 0.5, -1.0);
 +
    glVertex3f(-1.0, 0.5, 1.0);
 +
    glVertex3f(1.0, 0.5, 1.0);
 +
    glVertex3f(1.0, 0.5, -1.0);
 +
  glEnd;
 +
 
 +
Man kann deutlich erkennen, dass bei deaktiviertem Tiefentest das eigentlich vorne liegende rote Quad vom weiter hinten liegenden, allerdings später gerenderten, grünen Quad überdeckt wird.
 +
 
 +
== Transparenz und Tiefentest ==
 +
Sobald Transparenz verwendet wird, kann der Tiefentest zumindest für diese Teile nicht mehr benutzt werden. Dies würde zu Fehlern führen, da das transparente Fragment ggfs. das durchscheinende, jedoch erst später an die Grafik-Pipeline gesendete und somit vom Tiefentest verworfene Fragment dahinter völlig verbirgt. Wenn man Transparente Fragmente nicht in den Tiefenbuffer schreibt, so können diese Fragmente von anderen, eigentlich weiter hinten liegenden, Fragmenten komplett überschrieben werden.<br>
 +
Aus diesem Grund sollten Transparente Objekte nach allen anderen Objekten an OpenGL geschickt werden und diese möglichst von hinten nach vorne sortiert sein. Das Sortieren kann durch einen Alpha Wert im [[Framebuffer]] umgangen werden, jedoch ist dieser nur selten in Hardware vorhanden, wodurch in den weit langsameren Softwaremodus gewechselt wird.

Aktuelle Version vom 20. Februar 2010, 12:50 Uhr

Bei der Berechnung eines Fragmentes, muss OpenGL feststellen können, welche Teile der Grafik im Vordergrund stehen und welche Objekte ganz oder teilweise durch andere Objekte verdeckt werden.

Funktionsweise

Für den Tiefentest existiert der Tiefenpuffer(ZBuffer). Dieser ist genauso gross wie der Framebuffer. Für jeden Pixel auf dem Bildschirm gibt es dann also einen weiteren Wert - den Eintrag im Tiefenpuffer.

Im ersten Schritt wird der gesamte Tiefenpuffer auf den größtmöglichen Wert initialisiert (also die maximale Distanz, siehe glClear und glClearDepth). Sobald dann ein Objekt gerendert wird, wird für jeden Pixel überprüft, ob der Tiefenwert des Pixels dieses Objekts kleiner ist als der Wert im Tiefenpuffer. Ist dies der Fall, wird dieses Pixel in den Framebuffer gezeichnet und der Tiefenpuffer aktualisiert, ansonsten wird es verworfen. Der Test kann mittels glDepthFunc auch abgeändert werden. Zum Beispiel kann man dafür sorgen, dass auch gleich große Tiefenwerte akzeptiert werden.

Der Tiefentest findet verhältnismäßig früh in der Fragment-Pipeline statt, wodurch er sehr effizient nicht benötigte Fragmente ausschließen kann. Wenn Objekte tiefensortiert (in diesem Fall von vorne nach hinten) gezeichnet werden, ist der Tiefentest sehr gut geeignet um sehr früh nicht benötigte Fragmente zu verwerfen um so die Füllrate zu schonen.

Den Tiefentest aktiviert man mit glEnable(GL_DEPTH_TEST) und deaktiviert ihn entsprechend mit glDisable.

Außerdem kann man mit glDepthMask einstellen, ob beim Rendern des momentanen Objektes der Tiefenbuffer beschrieben werden soll oder nicht.

Aufteilung des Tiefenpuffers

Was steht im Tiefenpuffer? Im Tiefenpuffer steht nicht die Tiefe sondern die Tiefenzone. Beim erstellen der Renderkontexts wird angegeben, wie viele Bits für den ZBuffer zur Verfügung stehen. Bei 1Bit, gibt es genau 2^1=2 Tiefenzonen. Bei 4Bit 2^4=16 und bei 24Bit 2^24=16,7Mio Tiefenzonen. Der Bereich zwischen der Near- und Far-Clippingplane wird nun auf diese Zonen aufgeteilt. Fallen zwei Fragmente in die selbe Zone, entscheidet das durch glDepthFunc eingestellte Verhalten welches Fragment den Platz behalten/einnehmen darf.

Ein simples Beispiel

Info DGL.png Die Werte wurden in diesem Beispiel so gewählt, dass ganze Zahlen herauskommen. Das ist in der Realität sehr unwahrscheinlich.

ZNear liegt bei 1, ZFar liegt bei 257. Das heißt der Renderbereich ist 256 Einheiten groß. Dem ZBuffer wurden nur 4Bit gegönnt (eine sehr niedrige Auflösung). Das heißt 16 Zonen. Jede Tiefenzone ist (bei linearer Aufteilung) 256/16=16 OpenGL Einheiten tief.

Wird nun ein Fragment erzeugt, welches den Abstand z=17 zur Kamera hat (also Zone 2 von vorn) und ein anderes Fragment den Abstand z=35 hat (Zone 3) ist klar, welches Fragment näher ist. Nehmen wir an es kommt ein drittes Fragment hinzu mit dem Abstand 20. Für uns ist klar, dass dieses hinter dem ersten Fragment gehört. Allerdings ist der ZBuffer nicht fein genug aufgelöst, um das genauso zu sehen. Für ihn fällt auch dieses Fragment in Zone 2 und unter Umständen entscheidet die glDepthFunc Einstellung nun sogar, dass das neue Fragment das alte überschreiben darf. D.h. das 3. Fragment ist sichtbar obwohl es verdeckt sein sollte!

Zu beachten ist, dass nicht Bitanzahl des ZBuffers allein, die Auflösung bestimmt. Auch die Distanz zwischen den beiden Clippingplanes spielt eine Rolle. Je größer diese wird, desto grober wird der ZBuffer aufgelöst.


Beispiel

Aktivierter Tiefentest
Deaktivierter Tiefentest

Dieser Rendercode wird einmal mit und einmal ohne Tiefentest verwendet.

  glBegin(GL_QUADS);
    glColor4f(1.0, 0.5, 0.5, 1.0);
    glVertex3f(-1.0, 0.0, -1.0);
    glVertex3f(-1.0, 0.0, 1.0);
    glVertex3f(1.0, 0.0, 1.0);
    glVertex3f(1.0, 0.0, -1.0);

    glColor4f(0.5, 1.0, 0.5, 1.0);
    glVertex3f(-1.0, 0.5, -1.0);
    glVertex3f(-1.0, 0.5, 1.0);
    glVertex3f(1.0, 0.5, 1.0);
    glVertex3f(1.0, 0.5, -1.0);
  glEnd;

Man kann deutlich erkennen, dass bei deaktiviertem Tiefentest das eigentlich vorne liegende rote Quad vom weiter hinten liegenden, allerdings später gerenderten, grünen Quad überdeckt wird.

Transparenz und Tiefentest

Sobald Transparenz verwendet wird, kann der Tiefentest zumindest für diese Teile nicht mehr benutzt werden. Dies würde zu Fehlern führen, da das transparente Fragment ggfs. das durchscheinende, jedoch erst später an die Grafik-Pipeline gesendete und somit vom Tiefentest verworfene Fragment dahinter völlig verbirgt. Wenn man Transparente Fragmente nicht in den Tiefenbuffer schreibt, so können diese Fragmente von anderen, eigentlich weiter hinten liegenden, Fragmenten komplett überschrieben werden.
Aus diesem Grund sollten Transparente Objekte nach allen anderen Objekten an OpenGL geschickt werden und diese möglichst von hinten nach vorne sortiert sein. Das Sortieren kann durch einen Alpha Wert im Framebuffer umgangen werden, jedoch ist dieser nur selten in Hardware vorhanden, wodurch in den weit langsameren Softwaremodus gewechselt wird.