U N I V E R S I T Ä T KOBLENZ · LANDAU
(6) Polygon Clipping
Vorlesung
„Computergraphik I“
S. Müller
Wiederholung I
Clipping
Berechnung eines Ausschnitts
Test der Gültigkeit der Parameter bei Putpixel zu teuer, daher Clipping z.B. mit Bildschirmfenster
2-Stufiges Vorgehen:
• Schneller Test, ob Linie innerhalb/außerhalb des Bildschirms liegt
• Berechnung des Schnittpunkts nur, wenn unbedingt nötig.
Cohen-Sutherland
U N I V E R S I T Ä T
KOBLENZ · LANDAU S. Müller - 3 -
Wiederholung II
Durch einfache Bit-Operationen (und, oder) lässt sich schnell
erkennen, ob eine Linie vollständig innerhalb bzw. außerhalb des
Bereichs liegt.
1.
2.
3.
4.
5.
0
& 2
1 c ≠
c
0
| 2
1 c = = c
0000
0100 0110
0101
1001 1000 1010
0001 0010
Linie vollständig außerhalb, fertig.
Linie vollständig innerhalb, alles zeichnen.
Sonst: neue Punkte
bestimmen bis 3) erreicht ist
A B
C
D E
F
G
H
cut
c
G| c
H 0110c
G&c
H 0000draw
c
E| c
F 0000c
E&c
F 0000cut
c
C| c
D 0100c
C&c
D 0000ignore
c
A&c
B 1000Wiederholung III
{
c1 = outcode( ax, ay);
c2 = outcode( bx, by);
while((c1 | c2) != 0) {
if ((c1 & c2) != 0) return ;
a = ay-by; b = bx-ax; d = ax*by-ay*bx;
if (c1 == 0) c = c2;
else c = c1;
if ((c & C_LEFT) != 0)
{ x = xmin; y = -(a*x+d)/b;}
else if ((c & C_RIGHT) != 0) { x = xmax; y = -(a*x+d)/b;}
else if ((c & C_TOP) != 0) { y = ymax; x = -(b*y+d)/a;}
else if ((c & C_BOTTOM) != 0) { y = ymin; x = -(b*y+d)/a;}
if (c == c1)
{ ax = x; ay = y; c1 = outcode(ax, ay);}
else
{ bx = x; by = y; c2 = outcode(bx, by);}
A (Oben, Links)
B (0000) A‘ (Oben)
A (Oben, Rechts)
B (0000) A‘ (0000) A‘‘ (0000)
A (Unten)
B (Rechts)
A‘ (Rechts)
U N I V E R S I T Ä T
KOBLENZ · LANDAU S. Müller - 5 -
Wiederholung IV: Cohen-Sutherland
Dieser Algorithmus arbeitet sehr effizient
Er findet vor allem für Clipping an Bildschirmkanten Verwendung
Nachteil: er funktioniert nur für rechteckige Ausschnitte
für beliebige Ausschnittsfenster ist der Bereichscode so nicht mehr einsetzbar;
das gleiche gilt für die einfache Berechnung der Schnittpunkte zwischen den Kanten der Ausschnittsfenster und den Linien
Erweiterung für beliebige Ausschnittsfenster: Cyrus-
Beck
Wiederholung V: Cyrus-Beck
Clipping von Linien, an beliebigen, konvexen Polygonzügen
Eckpunkte des Clip-Objekts müssen gegen den
Uhrzeigersinn definiert werden („counterclockwise“)
Degenerierte Kanten (identische Eckpunkte) müssen vorher
abgefangen werden
Berechnung der Normalen auf die Kanten des Clip-Objekts, wobei Normalen nach außen zeigen
Start- und Endpunkt der zu zeichnenden Linie werden für jede Kante mit Hilfe eines
In/Out-Tests klassifiziert
Skalarprodukt zwischen Normale und Verbindung Kante-Linienpunkt
Beiden innen: weitermachen ohne SP Berechnung
Beide außen: aufhören.
Falls SP berechnet wird, wird
dieser ebenfalls als In/Out-
Punkt klassifiziert.
U N I V E R S I T Ä T
KOBLENZ · LANDAU S. Müller - 7 -
Wiederholung VI: Cyrus-Beck
void clipAndDraw(Vector2D a, Vector2D b) {
Vector2D v,w0,w1,n;
float t, tIn, tOut, c0, c1, nenner;
int i;
v = b - a;
tIn = 0.0;
tOut = 1.0;
for (i=0; i<4; i++) {
int iNext = (i+1) % 4;
n.setX( clipPoints[iNext].y() - clipPoints[i].y() );
n.setY( - (clipPoints[iNext].x() - clipPoints[i].x()) );
w0 = a - clipPoints[i];
w1 = b - clipPoints[i];
c0 = dot(w0, n);
c1 = dot(w1, n);
if (c0 > 0 && c1 > 0)
// alles außerhalb, fertig return;
if (c0 <= 0 && c1 <= 0) // innerhalb, weitertesten
continue;
nenner = dot(v,n );
t = -c0/nenner;
if (nenner < 0 && t > tIn) tIn = t;
if (nenner > 0 && t < tOut) tOut = t;
}
if (tIn > tOut) return;
b = a + tOut * v;
a = a + tIn * v;
drawLine(a, b);
}
line_cyrus_beck.vcproj
Wiederholung VII: Cyrus-Beck
A
B In
Out
A
B
In In
Out
Out
Die klassifizierten Schnittpunkte (In/Out) werden entlang von t sortiert. Ist
t_in (t für äußersten In-Punkt) größer als t_out (t für innersten Out-Punkt),
dann liegt die Linie außerhalb und muss nicht gezeichnet werden.
U N I V E R S I T Ä T KOBLENZ · LANDAU
Polygon Clipping
Ausgangspunkt
Wir haben gesagt, dass die „Applikation“ sicherstellen muss, dass put_pixel(x,y) nur für gültige Pixel
aufgerufen wird.
Bisher Clipping von Linien:
Cohen-Sutherland: Clippen von Linien an rechteckigen Objekten mit Hilfe von Bereichscodes.
Cyrus-Beck: Clippen von Linien an beliebigen, konvexen Polygonzügen
Clipping von Polygon gegen Polygon:
Sutherland-Hodgman
U N I V E R S I T Ä T
KOBLENZ · LANDAU S. Müller - 11 -
Sutherland-Hodgman
Clipping eines Polygonzugs gegen ein konvexes Clip- Polygon (z.B. Bildschirm); der Polygonzug darf konkav sein.
Bildschirm Polygonzug
(4 Punkte)
Ergebnis (9 Punkte)
Vorgehen
Der Polygonzug wird der Reihe nach an den Clip-Kanten geschnitten.
U N I V E R S I T Ä T
KOBLENZ · LANDAU S. Müller - 13 -
Vorgehen
Das Ergebnis muss wieder ein geschlossener Polygonzug sein.
Als Eingabe dient die Liste der Eckpunkte in der richtigen Reihenfolge (gegen den Uhrzeigersinn).
Die Routine gibt – nach dem Schnitt mit der jeweiligen Clip-Kante - eine neue Liste von Eckpunkten zurück.
Wichtig: die Reihenfolge muss stimmen.
4 Fälle
A
B A
B
A B
A
B
Beide Punkte drinnen:
Output B
Annahme: der Startpunkt A wurde bereits behandelt
Linie „zeigt“ nach außen:
Output S
Linie „zeigt“ nach innen:
Output S, B
Beide Punkte draußen:
S
S
Output (nichts)
U N I V E R S I T Ä T
KOBLENZ · LANDAU S. Müller - 15 -
Beispiel 1
A
B C
D
Input:
Output:
A B C D A B C S 2 S 1
S 2
S 1
Man beginnt mit der Kante letzter Punkt (D) – erster Punkt (A)
Beispiel 2
Input:
Output: S 1 A C
A
B C
S 2
S 1
S 4
S 5
S 6
S 3
S 4 S 5 S 6 S 3
A B C S 2
S 1
U N I V E R S I T Ä T
KOBLENZ · LANDAU S. Müller - 17 -
PseudoCode
Diese Routine wird der Reihe nach für jede Clip-Kante
aufgerufen.
„Innerhalb/Außerhalb“: hier wird wieder die Normale auf die
Kante berechnet und mit Hilfe des Vorzeichens des
Skalarproduktes entschieden.
„Output“: stellt ein neues
Polygon zusammen, das am Ende zurückgegeben wird.
„Schnittpunkt“ berechnet den Schnittpunkt mit der Clipkante, wobei nur Punkte zw. A und B berechnet werden dürfen.
Polygon SutherlandHodgman(Polygon poly, Edge clipedge)
{
A ist letzter Punkt des Polygons;
Schleife über alle Ecken B des Polygons
{
Wenn B innerhalb von clipedge Wenn A innerhalb von clipedge Output (B);
else {
S = Schnittpunkt (A,B,edge);
Output(S), Output(B);
} else
Wenn A innerhalb von clipedge {
S = Schnittpunkt (A,B,edge);
Output(S);
} A = B;
}
Ergebnis von Output zurückgeben.
}
Beispiel
A
B C
D
Problem: unnötige Kanten mit Fläche Null…
U N I V E R S I T Ä T
KOBLENZ · LANDAU S. Müller - 19 -
Sutherland-Hodgman
Clipping eines Polygonzugs gegen ein konvexes Clip- Polygon (z.B. Bildschirm); der Polygonzug darf konkav sein.
Problem: unnötige Kanten können am Rand
entstehen, was nicht unbedingt zu Fehlern führt; aus Effizienzgründen können diese nachträglich gelöscht werden.
Lässt sich sehr gut als Pipeline in Hardware umsetzen.
Erweiterung für konkave Clip-Polygone:
Weiler-Atherton
Clippen von Texten und Bildern
Schrift besteht aus Kurven für Umrisse der Buchstaben
Diese werden in der Regel einmal gezeichnet und in verschiedenen Auflösungen als Pixelmuster/Bitmap
gespeichert
Diese Bitmaps werden als Rechtecke betrachtet und gegen das Clip-Objekt geschnitten und nur der sichtbare Teil gezeichnet.
Das gleiche gilt auch für Bilder.
a a
U N I V E R S I T Ä T KOBLENZ · LANDAU
Mausabfrage und
Polygon-Datenstruktur
Polygon
Ein Polygon ist ein „n-Eck“ und setzt sich aus einer Reihe von zusammenhängenden Linien zusammen.
Für eine korrekte Normalenberechnung wird vorausgesetzt, dass die Eckpunkte gegen den Uhrzeigersinn definiert sind.
Datenstruktur für ein Polygon (2-dimensional), z.B.:
Liste von Eckpunkten,
Für jeden Eckpunkt x,y-Koordinate
U N I V E R S I T Ä T
KOBLENZ · LANDAU S. Müller - 23 -
Polygon2D Klasse (Polygon2D.h)
# ifndef _POLYGON2D_H
#define _POLYGON2D_H
#include "Vector2D.h"
#include <vector>
using namespace std;
class Polygon2D { public:
Polygon2D();
~Polygon2D();
void addPoint(Vector2D point); // neuen Punkt anfuegen Vector2D getPoint(int i) const; // Punkt i zurueckliefern void deletePoints(); // alle Punkte loeschen int numPoints() const; // Anzahl Punkte
void draw() const; // Polygon zeichnen private:
vector<Vector2D> mPoints; // dyn. Array der Punkte };
#endif
Polygon2D Klasse (Polygon2D.cpp)
#include "Polygon2D.h"
#include <GL/glut.h>
Polygon2D::Polygon2D() {
}
Polygon2D::~Polygon2D() {
}
void Polygon2D::addPoint(Vector2D point) {
mPoints.push_back(point);
}
Vector2D Polygon2D::getPoint(int i) const {
return mPoints[i];
}
void Polygon2D::deletePoints() {
mPoints.clear();
}
int Polygon2D::numPoints() const {
return mPoints.size();
}
void Polygon2D::draw() const {
glBegin(GL_LINE_LOOP);
for (int i = 0 ; i < mPoints.size() ; i++) glVertex2f(mPoints[i].x(), mPoints[i].y());
glEnd();
}
U N I V E R S I T Ä T
KOBLENZ · LANDAU S. Müller - 25 -
GLUT Funktion zur Mausabfrage
GLUT stellt uns eine Callback-Funktion für die Maus zur Verfügung.
Diese wird aufgerufen, wenn eine Maustaste gedrückt wird.
void mouse (int button, int state, int x, int y)
Die Parameter:
button : GLUT_LEFT_BUTTON, GLUT_MIDDLE_BUTTON, GLUT_RIGHT_BUTTON (zeigt an, welche Knöpfe gedrückt wurden)
state: GLUT_UP, GLUT_DOWN (zeigt an, ob die Taste gedrückt oder losgelassen wurde).
x, y: Die Position des Mauszeigers beim Drücken der Taste
(gemessen von der linken oberen Ecke des Fenster !!!)
Beispielprogramm mit Mauseingabe
void mouse(int button, int state, int x, int y) {
Vector2D vertex( x, HOEHE - y);
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) poly.addPoint ( vertex) ;
else if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) poly.deletePoints();
glutPostRedisplay();
}
Wurde die linke Maustaste gedrückt, dann wird ein neuer Punkt zum Polygon hinzugefügt.
Wurde die rechte Maustaste gedrückt, so wird das Polygon gelöscht (genauer:
Liste auf Null Elemente zurückgesetzt).
Bildschirm x y
Maus x
y
U N I V E R S I T Ä T
KOBLENZ · LANDAU S. Müller - 27 -
Beispielprogramm mit Mauseingabe
#include "Vector2D.h"
#include "Polygon2D.h"
#include <GL/glut.h>
#define BREITE 520
#define HOEHE 520 Polygon2D poly;
void display( void) {
glClear (GL_COLOR_BUFFER_BIT);
glColor3f( 0.0, 0.0, 1.0);
poly.draw();
glFlush ();
}
void init( void) {
glClearColor (1.0, 1.0, 1.0, 0.0);
glOrtho(0, BREITE, 0, HOEHE, -1, 1);
}
void mouse( int button, int state, int x, int y)
{
s. letzte Folie }
int main(int argc, char** argv) {
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize( BREITE, HOEHE);
glutInitWindowPosition (100, 100);
glutCreateWindow („Polygoneingabe");
init ();
glutMouseFunc(mouse);
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
Poly_input.vcproj
Punkte und Linien in OpenGL
U N I V E R S I T Ä T
KOBLENZ · LANDAU S. Müller - 29 -
Punkte, Linien, Flächen
Alle bisher behandelten Algorithmen zum
Zeichnen von Punkten
Zeichnen von Linien (mit Clipping und linearer Interpolation von Eckpunktwerten z.B. Farbe)
…
… sind in OpenGL sehr effizient implementiert und auch entsprechend auf die Möglichkeiten der Graphik- Hardware abgebildet.
OpenGL bietet uns daher eine wichtige Bibliothek zur Erstellung von graphischen Systemen (API:
application programming interface)
Namenstypisierung
Funktionen mit verschiedenen Parametern tragen Typ und Dimension im Namen:
gl<FUNC>{1234}{b,ub,s,us,i,ui,f,d}[v](...)
1,2,3,4: Dimension der Argumente
b,ub,s,us,i,ui,f,d: Typ: GLbyte, GLubyte, etc. Sonderfall:
GLclampf, GLclampd
optional können Parameter auch als Vektor übergeben
werden, gekennzeichnet durch das abschließende v
U N I V E R S I T Ä T
KOBLENZ · LANDAU S. Müller - 31 -
Namenstypisierung
Beispiel: glColor{34}{b,ub,s,us,i,ui,f,d}[v](...)
GLfloat f[3] = { 0.0, 0.5, 1.0 };
GLubyte b[4] = { 0, 127, 255, 255 };
glColor3f( 0.0, 0.5, 1.0 );
glColor3fv( f );
glColor4ubv( b );
OpenGL: Punkte
OpenGL setzt Primitive aus einzelnen Eckpunkten zusammen
Genereller Ablauf:
glBegin( GL_… );
glVertex...(...);
...
glEnd();
Vorteil: keine eigenen Datenstrukturen nötig
U N I V E R S I T Ä T
KOBLENZ · LANDAU S. Müller - 33 -
Beispiel: Eckpunkte
float fpos[3]={ 10, 0, 0 };
unsigned short sx=10,sy=0,sz=0;
glVertex2i( 10, 0 );
glVertex3s( sx, sy, sz );
glVertex3fv( fpos);
glVertex4d( 10.0, 0.0, 0.0, 1.0 );
bestimmen alle den gleichen Punkt
Homogene Koordinate wird von der Applikation in der
Regel nicht verwendet. Wenn sie weggelassen wird
(3D-Vektor) wird sie als 1 angenommen.
Primitivspezifische Einstellungen
Punkte: Größe mittels
glPointSize( GLfloat size );
nicht jede Größe unterstützt, über glGet() abzufragen
Wenn Antialiasing abgeschaltet ist (default), dann ist der Punkt ein Quadrat von size*size Pixeln. Nicht-Integer Werte werden gerundet.
glDisable(GL_POINT_SMOOTH);
Wenn Antialiasing eingeschaltet ist, dann wird ein Kreis gezeichnet. Nicht-Integer Werte für size werden hier nicht gerundet.
glEnable(GL_POINT_SMOOTH);
U N I V E R S I T Ä T
KOBLENZ · LANDAU S. Müller - 35 -
Aktivieren / Inaktivieren
Viele Features müssen explizit aktiviert werden glEnable( GLenum feature );
zum Inaktivieren dient dann
glDisable( GLenum feature );
der Zustand kann abgefragt werden mittels glIsEnabled( GLenum feature );
die Konstanten können nicht verodert werden
Linien
Linien: Breite mittels
glLineWidth( GLfloat width );
ähnlich wie Punkte nicht jede Größe möglich
Wenn Antialiasing abgeschaltet ist (default), dann ist width die Breite der Linie in Pixeln (also unabhängig von
Fenstergröße…). Nicht-Integer Werte werden gerundet.
glDisable(GL_LINE_SMOOTH);
Wenn Antialiasing eingeschaltet ist, dann wird die Helligkeit der Pixel abhängig vom Bedeckungsgrad berechnet. Nicht- Integer Werte werden hier nicht gerundet.
glEnable(GL_LINE_SMOOTH);
U N I V E R S I T Ä T
KOBLENZ · LANDAU S. Müller - 37 -
Strichelungen
Gestrichelte Linien definierbar:
glLineStipple( GLint factor, GLushort pattern );
Muster aus der Binärdarstellung von pattern 8 4 2 1 8 4 2 1 8 4 2 1 8 4 2 1
0xaaaa
0x1010
factor vervielfacht die einzelnen Bits
0xaaaa 1
0xaaaa 3
muß mit glEnable( GL_LINE_STIPPLE ); aktiviert werden
void display(void) {
int i;
glClear (GL_COLOR_BUFFER_BIT);
glColor3f (0.0, 0.0, 0.0);
/* 1. Zeile rechteckige Punkte */
for (i = -9; i < 10; i ++) {
glPointSize(10+i);
glBegin(GL_POINTS);
glVertex3f(i/10.0, 0.75,0);
glEnd();
}
/* 2. Zeile Anti-aliased Punkte */
glEnable(GL_POINT_SMOOTH);
for (i = -9; i < 10; i ++) {
glPointSize(10+i);
glBegin(GL_POINTS);
glVertex3f(i/10.0, 0.25,0);
glEnd();
}
/* 3. Zeile 2 Linien mit untersch. Stipple */
glEnable (GL_LINE_STIPPLE);
glLineWidth (1.0);
glLineStipple (1, 0x0101); /* dotted */
glBegin(GL_LINES);
glVertex3f(-0.75, -0.25, 0);
glVertex3f( 0, -0.25, 0);
glEnd();
glLineStipple (1, 0x00FF); /* dashed */
glBegin(GL_LINES);
glVertex3f( 0, -0.25, 0);
glVertex3f( 0.75, -0.25, 0);
glEnd();
/* 4. Zeile 2 dicke Lin. m. untersch. Stipple */
glLineWidth (5.0);
glLineStipple (1, 0x0101); /* dotted */
glBegin(GL_LINES);
glVertex3f(-0.75, -0.75, 0);
glVertex3f( 0, -0.75, 0);
glEnd();
glLineStipple (1, 0x00FF); /* dashed */
glBegin(GL_LINES);
glVertex3f( 0, -0.75, 0);