U N I V E R S I T Ä T KOBLENZ · LANDAU
(7) Scan-Konvertierung
Vorlesung
„Computergraphik I“
S. Müller
U N I V E R S I T Ä T
Wdh I: Sutherland-Hodgman
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:
Output (nichts)
S
S
U N I V E R S I T Ä T KOBLENZ · LANDAU
Wdh. II
Der Polygonzug wird der Reihe nach an den Clip-Kanten geschnitten.
U N I V E R S I T Ä T
Wiederhol. III: Sutherland-Hodgman
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.
U N I V E R S I T Ä T KOBLENZ · LANDAU
Wdh. IV
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
U N I V E R S I T Ä T
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
U N I V E R S I T Ä T KOBLENZ · LANDAU
Kleiner Test…
Cohen-Sutherland Clipping:
Linie an einem Rechteck
Linie an einem konvexen Objekt
Linie an einem konkaven Objekt
Cyrus-Beck
Rechteck an Rechteck
Linie an einem konvexen Objekt
Linie an einem konkaven Objekt
Sutherland-Hodgman
Linie an einem konkaven Objekt
Polygonzug an einem konvexen Objekt
Polygonzug an einem
konkaven Objekt
U N I V E R S I T Ä T
Füllen von Flächen
U N I V E R S I T Ä T KOBLENZ · LANDAU
Mögliche Methode durch In/Out-Test
Das Verfahren von Cyrus- Beck stellt im Kern einen Test bereit, ob ein Punkt innerhalb eines (konvexen) Polygonszugs liegt oder nicht.
Mit Hilfe dieses Tests lässt sich auch ein Algorithmus zum Füllen von Polygonen entwickeln
Grobes Verfahren:
1. Bestimme das „umgebende Rechteck“ (bounding box) des Polygons
2. Gehe in einer Schleife über alle Pixel der Bounding Box
3. Bestimme für jedes Pixel, ob es außen liegt (dann
ignorieren)
4. Sonst wird die Farbe für das
Pixel gesetzt
U N I V E R S I T Ä T
(achsen-parallele) Bounding-Box
Ermittlung der minimalen, bzw. maximalen x- und y-
Koordinaten des Polygonzugs
void Polygon2D::computeBoundingBox( Vector2D &min, Vector2D &max) const {
unsigned int i;
min = mPoints[0];
max = mPoints[0];
for (i = 1; i < mPoints.size(); i++) {
if (mPoints[i].x() < min.x()) min.setX( mPoints[i].x());
else if (mPoints[i].x() > max.x()) max.setX( mPoints[i].x());
if (mPoints[i].y() < min.y()) min.setY( mPoints[i].y());
else if (mPoints[i].y() > max.y()) max.setY( mPoints[i].y());
} }
P0 P1
C
P2
xmin xmax
ymax
ymin
U N I V E R S I T Ä T KOBLENZ · LANDAU
In/Out Beispiel
P
0P
1P
2P
3A B
n
0n
1n
2n
30 - - - - 3
out in in out out
+ -
- E
- 0
- D
- -
- C
- +
+ B
- -
+ A
2 1
0
C
D E
w
0
: <
− w n
i(Punkt innerhalb)
0
: >
+ w n
i(Punkt außerhalb)
U N I V E R S I T Ä T
In/Out Beispiel II
P
0P
1A B
n
0n
1n
2C
D E
P
2out in in out out
+ -
- E
- 0
- D
- -
- C
- +
+ B
- -
+ A
2 1
0
U N I V E R S I T Ä T KOBLENZ · LANDAU
Implementierung
void Polygon2D::fill_polygon() const {
Vector2D min, max;
computeBoundingBox( min, max);
glBegin(GL_POINTS);
for (int x = (int) min.x(); x <= (int) max.x(); x++)
for (int y = (int) min.y(); y <= (int) max.y(); y++) {
if (in_out(Vector2D(x,y))) { glVertex2i(x,y);
} }
glEnd();
}
P0 P1
C
P2
xmin xmax
ymax
ymin
U N I V E R S I T Ä T
Implementierung
bool Polygon2D::in_out( Vector2D pixel) const {
Vector2D w, n;
float c;
unsigned int i;
for (i = 0; i < mPoints.size(); i++) {
n.setX( mPoints[(i+1)%mPoints.size()].y() - mPoints[i].y());
n.setY(-(mPoints[(i+1)%mPoints.size()].x() - mPoints[i].x()));
w = pixel - mPoints[i];
c = dot( w, n);
if (c > 0) {// alles außerhalb, Abbruch return false;
} }
return true;
}
Poly_fill.vcproj
U N I V E R S I T Ä T KOBLENZ · LANDAU
Beispiele
U N I V E R S I T Ä T
Konkaves Polygon
P
0P
1P
2P
3A B
n
0n
1n
2n
4C D
n
3P
4+ - - + 2
+ - +
- 3
out in out out
- -
- D
- -
- C
- -
- B
- -
- A
4 1
0
Ergebnis:
U N I V E R S I T Ä T KOBLENZ · LANDAU
Fazit
Mit Hilfe der In/Out Routine lässt sich ein Algorithmus zum Füllen von Flächen leicht implementieren.
Dieser setzt allerdings konvexe Polygone voraus
Dieser Algorithmus ist sehr ineffizient, da viele „leere“
Pixel der Bounding-Box betrachtet werden
Besser wäre, nur die Pixel innerhalb des Polygonzugs zu betrachten
Vorteil: jedes Pixel lässt sich unabhängig von den anderen Pixeln betrachten, was das Prinzip
interessant macht für Multi-Processing (ultimativ 1
Prozessor für jedes Pixel)
U N I V E R S I T Ä T
Polygone füllen
Vorheriger Ansatz mit in/out- Test hatte das Problem, dass alle Pixel in der Boundig-Box getestet wurden.
Besser: Scanline-Orientiert.
Man „scant“ das Bild entlang von Linien von y
minnach y
max Für jede Scanline bestimmt man die „Spans“, also den x
minund x
maxWert der
horizontalen Linie und füllt die dazwischen liegenden Pixel.
y max
y min
x
span
x
minx
maxU N I V E R S I T Ä T KOBLENZ · LANDAU
Scan-Konvertierung
Dieses Vorgehen wird auch Scan-Konvertierung genannt.
Es nutzt ein wichtiges Prinzip der Computergraphik aus, die Kohärenz
Die Idee dabei ist: die Berechnungen für benachbarte Pixel sind ähnlich. Man tastet sich in kleinen Schritten durch das Bild und geht davon aus, dass sich
zwischen 2 Pixeln recht wenig ändert (gleiche
Sichtbarkeit, gleiche Beleuchtung…), was sehr wichtig
ist für die Effizienz vieler CG-Algorithmen.
U N I V E R S I T Ä T
Implementierung
1. Berechne y
min, y
max2. Schleife über alle y-Werte zwischen y
minund y
max3. Berechne spans: Schneide alle Kanten mit der y-Linie und sortiere die x-Werte
4. Fülle die Pixel innerhalb der spans
y
x
span
x
minx
maxy max
y min
U N I V E R S I T Ä T KOBLENZ · LANDAU
Spanberechnung
Für jede Kante wird wieder die Geradengleichung
aufgestellt (y gegeben):
y
x
min
span
x x
maxy max
y min A
B
( B A )
t A
X = + ⋅ −
−
⋅ −
+
=
y y
x x
y x
a b
a t b
a a y
x
y y
y
a b
a t y
−
= −
1 0 ≤ t ≤
Schnittpunkt gültig, wenn:
)
( x x
x t b a
a
x = + ⋅ −
U N I V E R S I T Ä T
Klasse für Span
#ifndef _SPAN2D_H
#define _SPAN2D_H class Span2D
{
public:
Span2D();
~Span2D();
void setXmin(int);
void setXmax(int);
int xmin();
int xmax();
private:
int mXmin, mXmax;
};
#endif
Span2D Polygon2D::computeSpan( int y) const { float ax, ay, bx, by, x;
Vector2D min, max;
float t;
Span2D span;
computeBoundingBox( min, max);
span.setXmax(0);
span.setXmin(max.x());
for (unsigned int i = 0; i < mPoints.size(); i++) { ax = mPoints[i].x();
ay = mPoints[i].y();
bx = mPoints[(i+1)%mPoints.size()].x();
by = mPoints[(i+1)%mPoints.size()].y();
if (ay == by) continue;
t = (float)(y - ay) / (by - ay);
if (t >= 0.0 && t <= 1.0) { x = ax + t*(bx-ax);
if (x < span.xmin()) span.setXmin(x);
if (x > span.xmax()) span.setXmax(x);
} }
U N I V E R S I T Ä T KOBLENZ · LANDAU
Implementierung
void Polygon2D::fill() const { Vector2D min, max;
Span2D span;
//nur füllen wenn mehr als zwei Punkte if (mPoints.size() < 3) {
return;
}
computeBoundingBox( min, max);
glBegin(GL_POINTS);
for (int y = (int) min.y(); y <= (int) max.y(); y++) { span = computeSpan(y); // xmin, max
for (int x = span.xmin(); x <= span.xmax(); x++) { glVertex2i(x,y);
} }
glEnd();
} Poly_fill.vcproj
U N I V E R S I T Ä T
Farben für die Eckpunkte?
Im Prinzip interpolieren wird die x,y-Koordinaten entlang der Kanten und spans durch Berechnung des
Geradenparameter t ∈ (0,1)
Dadurch können auch
beliebige andere Parameter der Eckpunkte (z.B. Farben, Normalen etc.) interpoliert werden.
Man spricht hier auch von der bilinearen Interpolation
(linear: auf einer Geraden, bilinear: auf einer Ebene)
y
x t l
t r
y max
y min
F 2
t
F 3
F 1
) (
: F F 2 t F 3 F 2 links l = + l ⋅ −
) (
: F F 1 t F 2 F 1 rechts r = + r ⋅ −
) (
: F F l t F r F l
span = + ⋅ −
U N I V E R S I T Ä T KOBLENZ · LANDAU
Beispiele
U N I V E R S I T Ä T
Implementierung
#ifndef _SPAN2D_H
#define _SPAN2D_H class Span2D
{
public:
Span2D();
~Span2D();
//get/set-Methoden void setXmin(int);
void setXmax(int);
void setRmin(float);
void setRmax(float);
void setGmin(float);
void setGmax(float);
void setBmin(float);
void setBmax(float);
int xmin();
int xmax();
float rmin();
float rmax();
float gmin();
float gmax();
float bmin();
float bmax();
private:
int mXmin, mXmax;
float mRmin, mRmax, mGmin, mGmax, mBmin, mBmax;
};
#endif
U N I V E R S I T Ä T KOBLENZ · LANDAU
Implementierung
void Polygon2D::fill() const { Vector2D min,max;
Span2D span;
float t, r, g, b;
computeBoundingBox( min, max); // ymin, ymax glBegin(GL_POINTS);
for (int y = (int) min.y(); y <= (int) max.y(); y++) { span = computeSpan( y); // xmin, max
for (int x = span.xmin(); x <= span.xmax(); x++) { if (span.xmax() == span.xmin())
t = 0;
else
t = (float)(x-span.xmin())/(span.xmax()-span.xmin());
r = span.rmin() + t*(span.rmax()-span.rmin());
g = span.gmin() + t*(span.gmax()-span.gmin());
b = span.bmin() + t*(span.bmax()-span.bmin());
glColor3f( r, g, b);
glVertex2i( x, y);
} }
glEnd();
}
Poly_fill.vcproj
Span erweitert um Farbe
U N I V E R S I T Ä T
Konkave Polygone
Der Algorithmus lässt sich leicht für konkave Polygone erweitern.
Für jede Scanline gibt es nicht nur einen Span, sondern eine Liste von
Spans, genauer: eine Reihe von x-Koordinaten, die
aufsteigend sortiert werden.
1 3
2 7 13 x
7 9 y
11
A B
C D
E F span 1
x
minx
maxspan 2
x
minx
maxU N I V E R S I T Ä T KOBLENZ · LANDAU
Implementierung
Span muß erweitert werden, so daß Liste von Spans ermöglicht wird.
void Compute_spans( int y) {
Für alle Kanten {
Bestimme x,r,g,b für Start/Endpunkt der Kante;
Wenn Kante nicht waagrecht, berechne t;
Wenn (t>=0.0 && t<=1.0) {
Berechne x,r,g,b mit Hilfe von t;
Insert_span (x, r, g, b);
// Einsortieren in Spanliste bzgl. x }
} }
U N I V E R S I T Ä T
Impl. II
void fill_polygon() {
compute_bounding_box();
Schleife über alle y-Scanlines zwischen ymin und ymax;
{
Compute_spans(y);
Nimm 2 Elemente xmin und xmax aus Spanliste, solange welche vorhanden;
{
Schleife über alle x zw. xmin und xmax {
Berechne t-Parameter für x;
Interpolieren r,g,b mit Hilfe von t;
Fülle Pixel an Stelle x,y mit r,g,b;
} } } }
Poly_fill.vcproj
U N I V E R S I T Ä T KOBLENZ · LANDAU
Beispiele
U N I V E R S I T Ä T
Probleme
Die Anzahl der Schnittpunkte ist nicht unbedingt gerade…
Idee: „Vorzeichen“ der Kanten miterfassen (genauer: wenn im Umlaufsinn der Endpunkt
größeres y-Koordinate hat, als der Startpunkt, dann „+“ sonst
„–“).
Wenn neuer Punkt in Spanliste eingefügt wird, wird überprüft, ob der Punkt schon da ist:
2 4
2
+
+
− +
−
− 4
2
2
Falls ja und Vorzeichen sind gleich, dann wird er ignoriert
Falls ja und Vorzeichen sind
nicht gleich, dann eingefügt
U N I V E R S I T Ä T KOBLENZ · LANDAU
Polygone und Flächen in OpenGL
U N I V E R S I T Ä T
GLUT: Tastatur Callback
void keyboard( unsigned char key, int x, int y) {
switch (key) { case '1':
show = 1;
glutPostRedisplay();
break;
case '2':
show = 2;
glutPostRedisplay();
break;
default:
break;
} }
key liefert die gedrückte Taste
x, y die aktuelle Mausposition
U N I V E R S I T Ä T KOBLENZ · LANDAU
Primitivtypen in OpenGL
5.
3.
4. 2.
1.
GL_POINTS
5.
3.
4. 2.
1.
GL_LINE_STRIP
5.
3.
4. 2.
1.
GL_LINE_LOOP
5.
3.
4. 2.
1.
GL_POLYGON
1.
3.
2. 4.
5.
GL_LINES
6.
1.
3.
4. 2.
5.
GL_QUADS
8.
1.
2.
3. 4.
6.
GL_TRIANGLES
5.
6.
7.
Mehrfachprimitive
primitive.c
U N I V E R S I T Ä T
Füllmuster
Analog zu den Linien:
glPolygonStipple( GLuchar * pattern );
muß auch aktiviert werden
glEnable(GL_POLYGON_STIPPLE);
feste Größe: 32x32
abgelegt als Bitmuster von links unten nach rechts
oben
U N I V E R S I T Ä T KOBLENZ · LANDAU
Zusätzliche Eckpunktattribute
State machine: an jedem Eckpunkt wird der aktuelle Zustand verwendet, d.h. erst muß die Farbe etc.
definiert werden, dann der Eckpunkt
glBegin(GL_POLYGON);
glColor3f(1,0,0);
glVertex3f(0,0,0);
glVertex3f(1,0,0);
glVertex3f(.5,1,0);
glEnd();
U N I V E R S I T Ä T
Zusätzliche Eckpunktattribute
Änderungen pro Eckpunkt möglich
glBegin(GL_POLYGON);
glColor3f(0,0,1);
glVertex3f(0,0,0);
glColor3f(0,0,1);
glVertex3f(1,0,0);
glColor3f(1,0,0);
glVertex3f(.5,1,0);
glEnd();
U N I V E R S I T Ä T KOBLENZ · LANDAU
Farbinterpolation
Die Farbe muß nicht interpoliert werden
glShadeModel( {GL_FLAT,GL_SMOOTH} ); bestimmt, ob interpoliert wird
bei GL_FLAT bestimmt die Farbe des letzten Eckpunktes (Ausnahme GL_POLYGON: erster)
1.
5.
4.
3.
2.
6.
1.
5.
4.
3.
2.
6.
GL_FLAT
GL_SMOOTH
U N I V E R S I T Ä T
Polygone….
… werden in Dreiecke zerlegt und als Dreiecke dargestellt.
Bi-lineare Interpolation nicht mehr eindeutig
Abhängig davon, wie die Dreieckskante verläuft
r g r g
r g
r g
Gouraud.c
v1 v2 v1
v4 v3 v4
v2 v3
DrawPolygon(v1,v2,v3,v4)
DrawPolygon(v2,v3,v4,v1)
U N I V E R S I T Ä T KOBLENZ · LANDAU
GLUT: Idle und Special Funktion
void main(…) {
…
glutIdleFunc(idle);
glutSpecialFunc(special);
}
IDLE Funktion
Wird immer aufgerufen, wenn der Windowmanager
„nichts zu tun hat“.
Gute Verwendung:
automatische Objektbewegung
SPECIAL Funktion
Sondertasten wie z.B.
Pfeil-/Cursortasten.
void special( int key, int x, int y) {
switch (key) {
case GLUT_KEY_UP : speed += 0.001f;
break;
case GLUT_KEY_DOWN : speed -= 0.001f;
break;
} }
void idle( void) {
beta += 5.0;
glutPostRedisplay();
}
U N I V E R S I T Ä T
Front-/Backbuffer
Erlaubt flackerfreie Animationen
zeichnen swap
zeichnen
Statt wie bisher:
glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);
…
glFlush(); Benötigt man:
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
…
U N I V E R S I T Ä T KOBLENZ · LANDAU
Tiefen-Puffer
Für jedes Pixel: finde/zeichne das vorderste Polygon
… kann direkt mit Hilfe der z- Koordinate entschieden
werden
Für jedes Pixel (x,y)
gibt es einen Farbeintrag im Framebuffer
gibt es einen z-Eintrag im Z- Buffer
Vorgehen
Initialisiere anfangs Z-Buffer mit größtem, maximalen Wert
Schreibe rgb-Wert in
Framebuffer und z-Wert in Z- Buffer für (x,y) nur, wenn aktueller z-Wert kleiner ist, als der für das Pixel bereits Eingetragene
function setpixel( int x, int y, rgb c, float z) if (z < z-buffer[x, y])
{
z-buffer[x, y]= z;
screen[x, y] = c;
}
U N I V E R S I T Ä T
Tiefen-Puffer in OpenGL
Glut-Fenster anmelden
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB|GLUT_DEPTH);
Einschalten
glEnable(GL_DEPTH_TEST);
Wichtig: nicht nur Bildspeicher, sondern auch Tiefenpuffer löschen
glClear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
proj_z.vcproj