OpenGL.org 3Dsource.de: Infos rund um 3D-Computergrafik, OpenGL und VRML
space Telefon-Tarife space VDI - Arbeitskreis Fahrzeugtechnik space Beschallungsanlagen und Elektroakustik space Heise-News space

10 Clipping, Culling (Sichtbarkeitstest)

10.010 Wie kann ich feststellen, ob ein Vertex sichtbar ist oder nicht ?

Man kann den OpenGL Feedback Buffer nutzen, um festzustellen ob ein Vertex geclippt wird oder nicht. Nachdem man mittels glRenderMode(GL_FEEDBACK) in den Modus geschaltet hat, kann das Vertex an den Feedback Buffer als GL_POINTS übermittelt werden. Dann schaltet man mit glRenderMode(GL_RENDER) zurück und testet die Grösse des Feedback Buffers. Eine Grösse von Null kennzeichnet ein geclipptes Vertex.

Normalerweise sind die OpenGL Feedback Methoden langsam. Es kann deshalb sinnvoll sein, den Clipping Test manuell durchzuführen. Um das zu realisieren, sind zunächst 6 Flächengleichungen entsprechend den Begrenzungen des Clipping Volumes zu ermitteln. Diese werden dann durch die aktuelle ModelView Matrix transformiert. Ist der Abstand des Punktes zu einer oder mehreren Flächen kleiner Null, liegt er ausserhalb des Viewing Volumes und wird geclippt.

Dieses GLUT-Beispiel zeigt, wie sich dieser Test programmieren lässt.

Ein weiteres Beispiel nennt sich Frustum Culling in OpenGL.

10.020 Wie kann ich den Sichtbarkeitstest beschleunigen ?

OpenGL bietet keine direkte Unterstützung zur Ermittlung, ob ein bestimmtes Objekt von einem festgelegten Augpunkt sichtbar ist oder nicht. Braucht man diese Information (z.B. um komplexe Objekte nicht unnötig zu berechnen), muss man diesen Test manuell durchführen.
Die vorhergehende Frage zeigt, wie man das realisieren kann. Das dort zu findende Beispiel wurde mit Nate Robin's hervorragendem Tutorials kombiniert.

Abstraktere API's, wie z.B. Fahrenheits Large Model, können diese Funktionen unter Umständen bereits enthalten.

Die HP OpenGL Implementation enthält einen Clipping Test für einen umschliessenden Körper als Extension. Um diese Funktion zu nutzen, ist der Occlusion Test zu aktivieren, ein umschliessender Körper zu zeichnen und durch den Aufruf von glGetBooleanv() die Sichtbarkeit festzustellen.

10.030 Kann ich auch einen anderen als rechteckigen Viewport nutzen ?

Der OpenGL Stencil Buffer kann genutzt werden, um einen Bereich ausserhalb eines nicht-rechteckigen Viewports zu maskieren. Mit einem aktivierten Stencil Buffer und entsprechend gesetztem Stencil Test kann dann nur noch in den beliebig geformten Viewport gezeichnet werden. Normalerweise wird der Stencil (Schablonen-) Bereich nur einmal gezeichnet und die wechselnden Frames in den unmaskierten Bereich.

Genauso wie auch beim Tiefenbuffer muss das Programm auch beim Stencil Buffer nach dem Anlegen eines OpenGL Contexts zunächst die Verfügbarkeit prüfen. Als Beispiel:

    glEnable (GL_STENCIL_TEST); /* Stencil Test aktivieren */

    /* Vorbereitung, um ein einzelnes Bit in den Stencil Buffer ausserhalb des
       Viewports zu zeichnen */
    glStencilFunc (GL_ALWAYS, 0x1, 0x1);

    /* Die Geometrie ausserhalb des Viewports zeichnen */

    /* Der Stencil Buffer enthält nun ein gesetztes Bit im Bereich ausserhalb des Viewports */

    /* Vorbereitung, um die Szene innerhalb des Viewports zu zeichnen */
    glStencilFunc (GL_EQUAL, 0x0, 0x1);

    /* jetzt kann die Szene innerhalb des Viewports so oft wie notwendig gezeichnet werden */

Sobald das erste Bit im Bereich ausserhalb des Viewports gezeichnet wurde, kann das Programm die nächsten Objekte an beliebiger Stelle zeichnen. Liegt das Objekt ausserhalb des Viewports, ist der Stencil Buffer auf glStencilFunc(GL_EQUAL,0x1,0x1) zu setzen, liegt es innen, wird glStencilFunc(GL_EQUAL,0x0,0x1) benötigt.

Vergleichbare Resultate lassen sich auch nur mit Hilfe des Tiefenbuffers erzielen. Nachdem die gesamte Szene gezeichnet wurde, kann man den Tiefenbuffer zurücksetzen und den Bereich ausserhalb des Viewing Volumes durch weitere Primitive überdecken.

10.040 Wenn ein Vertex eines OpenGL Primitives ausserhalb des Fensters liegt, sind die Farben oder Texturen plötzlich verfälscht. Was passiert hier ?

Dafür gibt es zwei potentielle Ursachen.

Wenn ein Objekt teilweise ausserhalb des Fensters liegt, schneidet es oft auch die Grenzen des Viewing Volumes. OpenGL muss alle Objekte, die auch nur teilweise ausserhalb des Viewing Volumes liegen, clippen. Dieses betrifft nicht nur die geometrische Form, sondern auch den Farbverlauf, der im Clipping-Fall interpoliert wird. Diese Interpolation (Berechnung des vermutlichen Farbverlaufs) berücksichtigt zwar auch die Perspektive auf das Objekt, kann allerdings beim Rasterisieren zu Abweichungen führen. Die Ursache hierfür findet man darin, dass die Interpolation noch vor der Rasterisation durchgeführt wird.

Bei einigen OpenGL Implementationen werden die Texturkoordinaten perspektivisch nicht korrekt rasterisiert. Man kann diese (performancebringende) Einschränkung aber umgehen, indem man OpenGL dazu mit glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST); veranlasst. Dagegen treten Farbverfälschungen durch das Clippen in fast jeder OpenGL Implementation auf. Sind diese nicht hinnehmbar, kann also eine Textur als Ausweichlösung genutzt werden.

Ein zweiter Grund, dass Color oder Texture Mapping nicht korrekt bei geclippten Objekten arbeiten, kann darin zu finden sein, dass die ursprünglichen Farbverläufe nicht plan sind. "Nicht plan" bedeutet bei Farben, dass die 3 RGB Komponenten der Farbwerte aller beteiligten Vertices nicht in einer Ebene des 3D Farbraumes liegen (fasst man RGB Werte als Koordinaten auf, zeichnet sie als XYZ und kann man keine gemeinsame Ebene für alle Koordinaten finden, sind die Farbwerte nicht plan).
  2D Texturkoordinaten liegen in diesem Sinne immer in einer Ebene. Hier bezeichnet "nicht plan" den Umstand, dass eine angegebene Texturkoordinate nicht zur Form des Objektes passt.

Nicht plane Farben oder Texturkoordinaten treten bei Dreiecken nicht auf, aber sind bei GL_QUADS, GL_QUAD_STRIP und GL_POLYGON leicht möglich. Es gibt keinen "richtigen" Weg, um nicht auf einer Ebene liegende Werte einfach zu interpolieren. Spätestens die perspektivisch korrekte Interpolation kann Unterschiede zwischen der vollen und geclippten Ansicht bewirken. Um diese Probleme zu umgehen, sollten immer nur plan zueinander liegende Werte Verwendung finden.

10.050 Mir ist bekannt, dass das gesamte Objekt im sichtbaren Bereich liegt. Wie kann ich das Clipping hierfür vermeiden und dadurch die Performance erhöhen?

Die OpenGL Spezifikation beinhaltet keinen Mechanismus, um das Clipping im Viewing Volume abzuschalten. Dieses Clipping wird immer für jedes Vertex durchgeführt, das an die OpenGL übergeben wird.

Einige Implementationen unterstützen die GL_EXT_clip_volume_hint Extension. Ist diese Erweiterung verfügbar, setzt OpenGL nach dem Aufruf von glHint(GL_CLIP_VOLUME_CLIPPING_HINT_EXT,GL_FASTEST) voraus, dass das nachfolgende Objekt komplett im Viewing Volume liegt und ein Clipping Test nicht erforderlich ist. Das normale Clipping findet wieder statt, nachdem man den Aufruf mit dem Parameter GL_DONT_CARE durchführt. Ist das Clipping deaktiviert und liegt ein Objekt trotzdem ausserhalb des Viewing Volumes, ist das Verhalten undefiniert.

10.060 Wenn ich den Augpunkt nahe an ein Objekt verschiebe, beginnt dieses zu verschwinden. Wie kann ich die vordere Clippingfläche deaktivieren ?

Diese Möglichkeit besteht nicht. Falls ihr darüber nachdenkt, sollte man zunächst die Ursache des Problems erfassen:  Was ist, wenn der Blick auf einen Punkt in der Mitte der Szene gerichtet ist ? Vielleicht liegen dann einige Objekte hinter dem Betrachter und muss deshalb geclippt werden. Das Zeichnen der Szene unter diesen Bedingungen kann dann auch unerwünschte Resultate zur Folge haben.

Damit brauchbare Ergebnisse aus der Projektion und dem Tiefenbuffervergleich entstehen, darf die vordere Clippingebene zNear auch nicht auf Null gesetzt werden (durch diese wird definiert und Divison durch Null oder sehr kleine Werte funktioniert das nicht. zNear sollte also nicht kleiner als 1.0 sein).

Um Artifakte durch das Clipping zu vermeiden, muss das Programm die Position des Augpunkts relativ zur Szene ständig kontrollieren. Dadurch kann man sicherstellen, dass der Augpunkt nicht zu nahe an die Objekte der Szene gelangt. Eine Möglichkeit zur Kontrolle besteht in einer Kollisionsberechnung, Informationen findet man im Kapitel Verschiedenes, Abschnitt Kollisonen.

Falls sichergestellt ist, das kein Objekt ausserhalb des Viewing Volumes liegt, kann man auch die OpenGL Extensions zum Abschalten des Clipping Tests nutzen.

10.070 Wie kann ich ein glBitmap() oder glDrawPixels(), deren ursprüngliche Rasterposition ausserhalb des Fensters liegt, in der linken oder unteren Ecke zeichnen ?

Liegt die Rasterposition ausserhalb des Fensters, ist das auch oftmals ausserhalb des Viewing Volumes und damit zumindest teilweise unzulässig. Mit glBitmap und glDrawPixels dargestellte Primitive werden aber nur gezeichnet, wenn die angegebene Rasterposition im gültigen Bereich liegt. Da glBitmap/glDrawPixels von der angegebenen Position nach oben und rechts hin zeichnen, erscheint es unmöglich, an die untere oder linke Kante angelehnte Primitive zu verwirklichen.

Es gibt dafür aber einen Trick: Zunächst wird die Rasterposition auf einen gültigen Wert innerhalb des Viewing Volumes gesetzt. Dann den folgenden Aufruf:

    glBitmap (0, 0, 0, 0, xMove, yMove, NULL);

Das veranlasst die OpenGL, ein kein Bitmap zu zeichnen sondern nur die Rasterposition um xMove,yMove zu verschieben. Da nicht beachtet wird, ob die Rasterposition danach ausserhalb des Viewing Volumes liegt, kann nun mit glBitmap() oder glDrawPixels() das gewünschte Bild gezeichnet werden.

10.080 Warum arbeitet glClear() für Flächen ausserhalb des Scissor-Bereichs nicht ?

Die OpenGL Implementation legt fest, dass glClear() bei aktiviertem Scissor Test nur den Bereich innerhalb des Scissor Rechtecks löscht. Um das gesamte Fenster zu löschen, kann der folgende Code genutzt werden:

    glDisable (GL_SCISSOR_TEST);
    glClear (...);
    glEnable (GL_SCISSOR_TEST);

10.090 Wie arbeitet das Flächen-Culling ? Warum greift es nicht auf die Oberflächennormalen zurück ?

OpenGL's Face Culling berechnet die Ausrichtung der Fläche (die Drehrichtung anhand der Reihenfolge der angegebenen Vertices) im Bildschirmkoordinatensystem. Die Drehrichtung ist positiv, wenn die Vertices entgegen dem Uhrzeigersinn angegeben wurden (CCW counter clockwise) bzw. negativ im Uhrzeigersinn (CW clockwise). Das Programm kann aber glFrontFace() nutzen, um die Interpretation der Reihenfolge zu erzwingen (front facing oder back facing festlegen). Mit glCullFace() kann auch festgelegt werden, ob nur Vorder- und/oder auch Rückseite des Objektes bei abgewandter Orientierung gezeichnet werden soll. Aktiviert wird das performancebringende Culling mit glEnable(GL_CULL_FACE).

OpenGL berechnet das Culling aus zwei Gründen erst im Bildschirmkoordinatensystem. Um vorteilhafte Lichteffekte zu erhalten, werden oft Normalenvektoren angegeben, die nicht zwangsweise orthogonal zur Fläche liegen. würden diese Normalen zur Berechnung des Culling genutzt, könnten einige Flächen irrtümlich ausgeblendet werden. Ausserdem könnte ein aus dem Kreuzprodukt zweier Vektoren berechneter Normalenvektor für das Culling eine Matrixinversion benötigen, was nicht immer realisiert werden kann (z.B. singuläre Matrix). Dagegen ist die Drehrichtung im Bildschirmkoordinatensystem immer definiert.

Es gibt aber auch einige OpenGL Implementationen, die die GL_EXT_ cull_vertex Extension unterstützen. Ist diese Extension vorhanden und legt man den Augpunkt im Modellkoordinatensystem mit homogenen Koordinaten fest, werden alle Flächen ausgeblendet, bei denen das Kreuzprodukt von Normalenvektor und Vertex-Augpunkt-Vektor in Blickrichtung zeigt (also vom Betrachter weg). Zeigen alle so berechneten Vektoren eines Objekts vom Betrachter weg, wird dieses nicht mehr zur Anzeige gebracht. Damit kann ein deutlicher Geschwindigkeitsvorteil erzielt werden, weil diese Berechnung frühzeitig in der Rendering Pipeline durchgeführt wird.

Seite durchsuchen nach:

Fragen oder Ideen an:
Thomas.Kern@3Dsource.de
Linux-Counter
(C) Thomas Kern