Silhouette: Unterschied zwischen den Versionen

Aus DGL Wiki
Wechseln zu: Navigation, Suche
K (Anmerkung)
K ("unvollständig" entfernt)
 
(6 dazwischenliegende Versionen von 5 Benutzern werden nicht angezeigt)
Zeile 6: Zeile 6:
 
== Wie berechne ich Sie? ==
 
== Wie berechne ich Sie? ==
 
Ganz einfach. Mit Mathematik!<br>
 
Ganz einfach. Mit Mathematik!<br>
Wenn man die '''Silhouette''' berechnen will benötigt man folgende Dinge:
+
Wenn man die '''Silhouette''' berechnen will, benötigt man folgende Dinge:
 
* 2 benachbarte Flächen vom Objekt, bei denen man untersuchen will ob ihre Zwischenkante zur '''Silhouette''' gehört.
 
* 2 benachbarte Flächen vom Objekt, bei denen man untersuchen will ob ihre Zwischenkante zur '''Silhouette''' gehört.
 
* Die Position des Betrachters.
 
* Die Position des Betrachters.
* Die Position auf die der Betrachter sieht. (Intelligenterweise, dass Zentrum des Objekts dessen '''Silhouette''' man berechnen will.)
+
* Die Position auf die der Betrachter sieht. (Intelligenterweise das Zentrum des Objekts, dessen '''Silhouette''' man berechnen will.)
 
* Wissen wie man [[Normalen]] berechnet.
 
* Wissen wie man [[Normalen]] berechnet.
  
Zeile 22: Zeile 22:
  
 
Außerdem sollten Kanten die nur zu einem Polygon gehören ebenfalls zur Silhouette gezählt werden.
 
Außerdem sollten Kanten die nur zu einem Polygon gehören ebenfalls zur Silhouette gezählt werden.
 +
 +
 +
Die [[GLU]] bietet die Function [[gluQuadricDrawStyle]] welche per GLU_SILHOUETTE die Möglichkeit bietet Silhouetten von [[Quadrik]]en zu erzeugen. Allerdings unterscheidet sich die Definition von "Silhoute" in der GLU von der obigen. Dort werden alle Coplanaren Flächen miteinadner verbunden. Dies führt z.B. bei Kugeln zu abweichenden, und mitunter unbrauchbaren Silhouetten.
  
 
== Wo benötige ich Sie? ==
 
== Wo benötige ich Sie? ==
 
Die '''Silhouette''' wird zum Beispiel bei Schattenberechnungen benötigt. ([[Volumetrische Stencilschatten]])<br>
 
Die '''Silhouette''' wird zum Beispiel bei Schattenberechnungen benötigt. ([[Volumetrische Stencilschatten]])<br>
 
Hin und wieder möchte man aber auch einfach nur die '''Silhouette''' eines Objektes nachzeichnen.<br>
 
Hin und wieder möchte man aber auch einfach nur die '''Silhouette''' eines Objektes nachzeichnen.<br>
 +
 +
== Weitere Verfahren zur Berechnung der Silhouette ==
 +
Neben der Möglichkeit die Silhouette in Software zu berechnen, kann man sich die Arbeit auch von OpenGL abnehmen lassen. Diese Methoden sind allerdings nicht so flexibel und eignen sich daher meist nur für die einfache Darstellung der Silhouette.
 +
 +
== Mit Hilfe des Tiefenpuffers ==
 +
Bei dieser Methode bedienen wir uns des Tiefenpuffers von OpenGL um automatisch die Teile des Objektes übermalen zu lassen, die nicht zur '''Silhouette''' gehören. Dazu zeichnen wir das Objekt einmal ganz normal. Danach zeichnen wir die nach hinten zeigenden Polygone des Objektes noch einmal als Gitternetz. Die Teile des Gitternetzes die deutlich hinter dem Objekt liegen werden vom Tiefentest automatisch aussortiert. Für die Teile die genau auf den Außenkanten liegen wird entweder eine entsprechende Tiefenfunktion (GL_LEQUAL) oder eine ausreichende Liniendicke benötigt.
 +
[[bild:Silhouette_01.jpg|right]]
 +
<source lang="cpp">
 +
glPushAttrib(GL_ALL_ATTRIB_BITS);
 +
 +
zeichne_objekt();
 +
 +
glDisable(GL_LIGHTING);
 +
glPolygonMode(GL_BACK,GL_LINE);
 +
glCullFace(GL_FRONT);
 +
 +
zeichne_objekt();
 +
 +
glPopAttrib();
 +
</source>
 +
Wie auf dem Bild zu sehen ist, werden mit dieser Methode auch teilweise Linien gezeichnet, die nicht wirklich Außen liegen. Um dies zu verhindern kann man die beiden Instanzen des Objektes mittels [[glDepthRange]] in verschiedene Bereiche des Tiefenpuffers zeichnen. Das eigentliche Objekt z.B. in den Bereich 0-0.5 und das Gitternetz in den Bereich 0.5-1. Dadurch wird sicher gestellt, dass das Gitternetz an keiner Stelle vor dem Objekt gezeichnet wird.
 +
 +
== Mit Hilfe des Stencilpuffers ==
 +
Bei dieser Methode wird das Objekt im ersten Durchgang genutzt um den Stencilpuffer überall dort zu füllen, wo es gezeichnet wird. Anschließend werden wieder die hinteren Polygone als Gitternetz gezeichnet. Allerdings wird diesmal durch einen Stenciltest dafür gesorgt, dass die Linien nur dort gezeichnet werden, wo der Stencilpuffer den Wert 0 hat.
 +
[[bild:Silhouette_02.jpg|right]]
 +
<source lang="cpp">
 +
glPushAttrib(GL_ALL_ATTRIB_BITS);
 +
 +
// Stenciltest aktivieren
 +
glEnable(GL_STENCIL_TEST);
 +
// Stenciltest immer erfolgreich (alles zeichnen)
 +
glStencilFunc(GL_ALWAYS,0,~0);
 +
// wenn z-test erfolgreich => inc stencilbuffer
 +
glStencilOp(GL_KEEP,GL_KEEP,GL_INCR);
 +
 +
zeichne_objekt();
 +
 +
// nur zeichnen wo stencilbuffer = 0
 +
glStencilFunc(GL_EQUAL,0,~0);
 +
// stencilbuffer nicht verändern
 +
glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP);
 +
 +
glDisable(GL_LIGHTING);
 +
glPolygonMode(GL_BACK,GL_LINE);
 +
glCullFace(GL_FRONT);
 +
 +
zeichne_objekt();
 +
 +
glPopAttrib();
 +
</source>
 +
Bei dieser Methode werden nur Linien gezeichnet, die auch wirklich außen liegen.
 +
 +
== Selber berechnet ==
 +
Diese Methode funktioniert im Grunde wie die Grundtechnik. Allerdings möchte ich hier noch eine kleine Änderung aufführen, die es einem ermöglicht auch Linien zu zeichnen die direkt auf dem Objekt liegen. Dies ist z.B. nützlich für Cartoonshading, bei dem Bereiche ähnlicher Beschaffenheit gleich eingefärbt werden. Hierzu bedienen wir uns diesmal nicht dem Blickvektor um zu bestimmen welche Linien wir zeichnen, sondern legen einen Schwellwert für den Winkel, der zwischen den Normalen zweier benachbarter Polygone liegt, fest. Wird dieser Schwellwert überschritten, zeichnen wir die Schnittkante der Polygone.
 +
[[bild:Silhouette_03.jpg|right]]
 +
<source lang="cpp">
 +
draw line if(dot(n1,n2) <= threshold)
 +
</source>
 +
Dieser Schwellwert ist individuell und je nach Situation zu wählen. Diese Methode ist gegebenenfalls mit anderen Methoden zu kombinieren um auch die außen liegenden Linien zu zeichnen, da diese von dieser Methode nicht zwingend mit berechnet werden.<br>
 +
<br>
 +
Wer noch mehr Klarheit braucht kann sich hier eine Demo, die alle Verfahren zeigt herunterladen:
 +
[http://shaijan.delphigl.com/index.php?p=silhouette_de]
 +
[[Kategorie:Anleitung]] [[Kategorie:Technik_oder_Algorithmus]]

Aktuelle Version vom 14. April 2010, 08:03 Uhr

Silhouette

Was ist das?

Die Silhouette ist der Linienzug/die Kante welche die sichtbaren Flächen von den Nichtsichtbaren trennt.

Wie berechne ich Sie?

Ganz einfach. Mit Mathematik!
Wenn man die Silhouette berechnen will, benötigt man folgende Dinge:

  • 2 benachbarte Flächen vom Objekt, bei denen man untersuchen will ob ihre Zwischenkante zur Silhouette gehört.
  • Die Position des Betrachters.
  • Die Position auf die der Betrachter sieht. (Intelligenterweise das Zentrum des Objekts, dessen Silhouette man berechnen will.)
  • Wissen wie man Normalen berechnet.

Und so geht man vor:

  1. Berechnen der Normalen für die Flächen F1 und F2. -> Normalen N1 und N2.
  2. Berechnen des Blickvektors V (Betrachterpos - Blickpunkt).
  3. Berechnen des Winkels zwischen N1 und V. -> Winkel W1.
  4. Berechnen des Winkels zwischen N2 und V. -> Winkel W2.

Wenn nun einer der beiden Winkel W1 oder W2 > 90° und der andere Winkel <= 90° ist, dann ist die Kante zwischen den Flächen F1 und F2 Teil der Silhouette des Körpers.

Außerdem sollten Kanten die nur zu einem Polygon gehören ebenfalls zur Silhouette gezählt werden.


Die GLU bietet die Function gluQuadricDrawStyle welche per GLU_SILHOUETTE die Möglichkeit bietet Silhouetten von Quadriken zu erzeugen. Allerdings unterscheidet sich die Definition von "Silhoute" in der GLU von der obigen. Dort werden alle Coplanaren Flächen miteinadner verbunden. Dies führt z.B. bei Kugeln zu abweichenden, und mitunter unbrauchbaren Silhouetten.

Wo benötige ich Sie?

Die Silhouette wird zum Beispiel bei Schattenberechnungen benötigt. (Volumetrische Stencilschatten)
Hin und wieder möchte man aber auch einfach nur die Silhouette eines Objektes nachzeichnen.

Weitere Verfahren zur Berechnung der Silhouette

Neben der Möglichkeit die Silhouette in Software zu berechnen, kann man sich die Arbeit auch von OpenGL abnehmen lassen. Diese Methoden sind allerdings nicht so flexibel und eignen sich daher meist nur für die einfache Darstellung der Silhouette.

Mit Hilfe des Tiefenpuffers

Bei dieser Methode bedienen wir uns des Tiefenpuffers von OpenGL um automatisch die Teile des Objektes übermalen zu lassen, die nicht zur Silhouette gehören. Dazu zeichnen wir das Objekt einmal ganz normal. Danach zeichnen wir die nach hinten zeigenden Polygone des Objektes noch einmal als Gitternetz. Die Teile des Gitternetzes die deutlich hinter dem Objekt liegen werden vom Tiefentest automatisch aussortiert. Für die Teile die genau auf den Außenkanten liegen wird entweder eine entsprechende Tiefenfunktion (GL_LEQUAL) oder eine ausreichende Liniendicke benötigt.

Silhouette 01.jpg
glPushAttrib(GL_ALL_ATTRIB_BITS);

zeichne_objekt();

glDisable(GL_LIGHTING);
glPolygonMode(GL_BACK,GL_LINE);
glCullFace(GL_FRONT);

zeichne_objekt();

glPopAttrib();

Wie auf dem Bild zu sehen ist, werden mit dieser Methode auch teilweise Linien gezeichnet, die nicht wirklich Außen liegen. Um dies zu verhindern kann man die beiden Instanzen des Objektes mittels glDepthRange in verschiedene Bereiche des Tiefenpuffers zeichnen. Das eigentliche Objekt z.B. in den Bereich 0-0.5 und das Gitternetz in den Bereich 0.5-1. Dadurch wird sicher gestellt, dass das Gitternetz an keiner Stelle vor dem Objekt gezeichnet wird.

Mit Hilfe des Stencilpuffers

Bei dieser Methode wird das Objekt im ersten Durchgang genutzt um den Stencilpuffer überall dort zu füllen, wo es gezeichnet wird. Anschließend werden wieder die hinteren Polygone als Gitternetz gezeichnet. Allerdings wird diesmal durch einen Stenciltest dafür gesorgt, dass die Linien nur dort gezeichnet werden, wo der Stencilpuffer den Wert 0 hat.

Silhouette 02.jpg
glPushAttrib(GL_ALL_ATTRIB_BITS);

// Stenciltest aktivieren
glEnable(GL_STENCIL_TEST);
// Stenciltest immer erfolgreich (alles zeichnen)
glStencilFunc(GL_ALWAYS,0,~0);
// wenn z-test erfolgreich => inc stencilbuffer
glStencilOp(GL_KEEP,GL_KEEP,GL_INCR);

zeichne_objekt();

// nur zeichnen wo stencilbuffer = 0
glStencilFunc(GL_EQUAL,0,~0);
// stencilbuffer nicht verändern
glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP);

glDisable(GL_LIGHTING);
glPolygonMode(GL_BACK,GL_LINE);
glCullFace(GL_FRONT);

zeichne_objekt();

glPopAttrib();

Bei dieser Methode werden nur Linien gezeichnet, die auch wirklich außen liegen.

Selber berechnet

Diese Methode funktioniert im Grunde wie die Grundtechnik. Allerdings möchte ich hier noch eine kleine Änderung aufführen, die es einem ermöglicht auch Linien zu zeichnen die direkt auf dem Objekt liegen. Dies ist z.B. nützlich für Cartoonshading, bei dem Bereiche ähnlicher Beschaffenheit gleich eingefärbt werden. Hierzu bedienen wir uns diesmal nicht dem Blickvektor um zu bestimmen welche Linien wir zeichnen, sondern legen einen Schwellwert für den Winkel, der zwischen den Normalen zweier benachbarter Polygone liegt, fest. Wird dieser Schwellwert überschritten, zeichnen wir die Schnittkante der Polygone.

Silhouette 03.jpg
draw line if(dot(n1,n2) <= threshold)

Dieser Schwellwert ist individuell und je nach Situation zu wählen. Diese Methode ist gegebenenfalls mit anderen Methoden zu kombinieren um auch die außen liegenden Linien zu zeichnen, da diese von dieser Methode nicht zwingend mit berechnet werden.

Wer noch mehr Klarheit braucht kann sich hier eine Demo, die alle Verfahren zeigt herunterladen: [1]