5. Zweidimensionale Zeichenalgorithmen und Grundlagen zur interaktiven Grafik
5.1. Grundlegende Rastergrafik-Algorithmen
Problem:
virtuelles Zeichenblatt mit stetigen Koordinaten (float x, float y)
Ziel 1: Linien sollen ihrem idealen Verlauf möglichst nahe kommen
Ziel 2: Ausgleich möglicher, verbleibender Störeffekte.
Zunächst zu Ziel 1:
Raster-Konvertierungen für Geraden(-stücke), Kreise, Ellipsen...
Zunächst: Linien mit Dicke von 1 Pixel darstellen
(aus Rauber 1993)
Naiver Algorithmus:
aus x-Werten von x1 bis xn die y-Werte nach
Geradengleichung y = mx+b berechnen und auf nächstgelegene Integer-Zahl
runden.
Nachteile:
möglichst einfache Operationen: wichtig auch für evtl. Hardware-Realisierung!
schrittweise Verbesserung (vgl. Übungsblatt 0, Aufg. 5):
wenn error >= 0.5 ist, setze Pixel um 1 höher und dekrementiere
error um 1. Damit optimale Nähe der gesetzen Pixel zur idealen Geraden gewährleistet.
Immer noch floating point-Operationen
Ergebnis:
Bresenham-Algorithmus zur Linienrasterung (1965)
void Linie (int x1, int y1, int xn, int yn,
int value)
/* Anfangspunkt (x1, y1), Endpunkt (xn, yn),
Steigung zwischen 0 und 1 angenommen */
{
int error, x, y, dx, dy;
dx = xn - x1; dy = yn - y1;
error = -dx/2; y = y1;
for (x = x1; x <= xn; x++)
{
set_pixel(x, y, value);
error += dy;
if (error >= 0)
{
y++;
error -= dx;
}
}
}
In obiger Form nur für 1. Oktanden.
Verallgemeinerung: Ausnutzung der Symmetrie.
"Treibende Achse" = Achse, deren Werte mit fester Schrittweite 1 durchlaufen werden
(im obigen Fall: x-Achse).
Fallunterscheidung nach dem Oktanden der Geraden:
Nachteil:
Linien unterschiedlicher Steigung erscheinen unterschiedlich hell.
Linie B ist sqrt(2)-mal länger als Linie A, hat aber dieselbe Zahl von Pixeln
Þ erscheint schwächer!Abhilfe: Intensität (Parameter
value) mit der Steigung der Linie erhöhen.Rasterung von Ellipsen
Parametermethode (auch: trigonometrische Methode):
Ellipsengleichung in Parameterform (Parameter
q = Winkel im Mittelpunkt):x = a cos
q , y = b sin q.q durchläuft die
Werte von 0 bis 360° bzw. bis 2p.
a und b sind die Abschnitte auf den Hauptachsen. Hier Hauptachsen = Koordinatenachsen.
Um den Winkel
f gedrehte Ellipse mit Mittelpunkt (xc, yc):y = a sin
f cos q + b cos f sin q + yc = C cos q + D sin q + ycDurchlaufe äquidistante Parameterwerte
verbinde die zugehörigen Ellipsenpunkte durch Linien (Geradensegmente).
n = Anzahl gewünschter Geradensegmente
Implementation:
void ellipse(float a, float b, float phi,
float xc, float yc, int n, int value)
{
float cosphi, sinphi, theta, pi, costheta,
sintheta, A, B, C, D, x0, x1, y0, y1, incr;
int i;
pi = 4*arctan(1);
cosphi = sintab[pi/2 + phi];
sinphi = sintab[phi];
A = a*cosphi; B = b*sinphi;
C = a*sinphi; D = b*cosphi;
x0 = A + xc; y0 = C + yc;
theta = 0; incr = 2*pi/n;
for (j=1; j <= n; j++)
{
theta += incr;
costheta = sintab[pi/2 + theta];
sintheta = sintab[theta];
x1 = A*costheta - B*sintheta + xc;
y1 = C*costheta + D*sintheta + yc;
Draw_line(x0, y0, x1, y1, value);
x0 = x1; y0 = y1;
}
}
Kosinusberechnung wird auf Sinus zurückgeführt.
Sinusberechnung wird hier durch Lookup-Table ersetzt.
Wegen sin x = sin(
Nachteil der Parametermethode:
Feste Unterteilung der Parameterwerte
anderer Ansatz:
Polynom-Methode
Ausgangspunkt jetzt:
kartesische Achsenabschnittsform der Ellipsengleichung (Polynom 2. Grades).
Ungedrehte Ellipse:
(x/a)² + (y/b)² = 1
Vorgehen:
Auflösen nach y, x durchläuft alle in Frage kommenden Werte in Einerschritten,
2 y-Werte werden jeweils berechnet.
Nachteil: hoher Rechenaufwand (Quadratwurzeln!).
alternativer Ansatz:
Scangeraden-Methode
(auch für andere Arten von Kurven)
Prinzip: horizontale Scangerade wird über die (ideale) Ellipse geschoben, für jede
y-Position werden die Schnittpunkte bestimmt und im Raster approximiert.
Rechnung geht wieder aus von der Achsenabschnittsform (s.o.)
Für gedrehte und verschobene Ellipsen: x und y entsprechend substituieren durch x cos
f - y sin f + xcyt,b =
± Ö (b² cos² f + a² sin² f ).Ferner: Ausnutzung der Symmetrie:
Nachteil der Methode:
In jeder Bildschirmzeile, die die Ellipse trifft (außer der oberen und unteren Tangente), werden
genau 2 Pixel gesetzt
Abhilfe:
Für letztere Vorgehensweise benötigt man die Parameterwerte mit
Tangentensteigung +1 und -1:
bei
Vorteil der Scangeraden-Methode:
Direkt auch zum Füllen von Ellipsen geeignet (verbinde die Schnittpunkte jeweils durch
horizontales Scangeraden-Stück).
weiterer Ansatz:
Differentielle Methode
Grundidee: wie bei der inkrementellen Geradendarstellung wird x um 1 erhöht und
y um ein (variables) Inkrement m.
Variante: man erhöht den Parameter
x' = -(a/b)*y, y' = (b/a)*x,
dx = -(a/b)*y*d
q , dy = (b/a)*x*dqDx = -(a/b)*y*Dq , Dy = (b/a)*x*Dq
Þ als Ansatz für die
Implementation würde sich anbieten:
x -= (a/b)*y*Dq ,
y += (b/a)*x*Dq .
Jedoch: schlechte Konvergenz, Dq
muss sehr klein sein, um genügende Genauigkeit zu erzielen.
Besser arbeitet man mit einer Zentrierung:
xi+1 = xi -
(a/b)(yi +
yi+1)/2 * Dq ,
yi+1 = yi + (b/a)(xi +
xi+1)/2 * Dq
das sind 2 Gleichungen mit 2 Unbekannten (xi+1 und yi+1), die
aufgelöst werden können.
Durch Zusammenfassen von Konstanten und Herausziehen von schleifeninvarianten Berechnungsteilen aus
der Hauptschleife kommt man auf 4 Multiplikationen und 2 Additionen pro errechnetem Ellipsenpunkt.
Verbesserung der Laufzeit möglich durch Verwendung des Differentials zweiter Ordnung, um
Pi+1 aus Pi und Pi-1 zu
berechnen (Formeln siehe Rauber 1993). Dann pro dargestelltem
Geradensegment nur 2 Multiplikationen und 2 Additionen.
Für gedrehte Ellipsen analog (Unterschied nur in der Initialisierung).
Nachteil des differentiellen Verfahrens in dieser Form: Darstellung ungenau an den steilen Stellen
der Ellipse.
Abhilfe: Dq eliminieren, in jedem
Schritt 1 Pixel der Ellipse berechnen (D x = 1), dabei die "treibende Achse" je nach Bereich der Ellipse wählen.
1: x treibende Achse, inkrementiere y.
2: y treibende Achse, inkrementiere x.
3: x treibende Achse, dekrementiere y.
4: y treibende Achse, dekrementiere x.
Komplexität: für jedes Pixel 1 Multiplikation, 1 Division, 4 Additionen erforderlich.
Kreise:
Spezialfälle der Ellipsen.
Für Kreise können (theoretisch) weitere Symmetrien ausgenutzt werden.
Beachte aber:
Für viele Monitore sind Höhe und Breite der Pixel ungleich
Wegen seiner Einfachheit erwähnen wir den Kreisalgorithmus von Bresenham (Analogie zum Geraden-Algorithmus):
für d > 0: x++; d += (4x + 6);
für d <= 0: x++; y--; d += (4(x-y)+10).
Beachte: Multiplikationen mit Zweierpotenzen lassen sich effizient als Shift-Operationen realisieren.
Damit kommt auch der Bresenham-Kreis-Algorithmus ohne "echte" Multiplikationen aus.
Pro Punkt: durchschnittl.
Füllen von Polygonen und geschlossenen Kurven
Die Scangeraden-Methode
Voraussetzung: geschlossenes Polygon P liegt in geometrischer Beschreibung vor.
(zur Paritätsregel: vgl. "even-odd Filling rule" bei PostScript!)
Es sind Sonderfälle zu beachten:
(Krömker 2001)
Nach der Regel für den ymax-Knoten einer Kante werden in diesem Beisp. für die Scangerade y=0 die Kanten a und b, für die Scangerade y=2 die Kanten c und d, für die Scangerade y=4 aber keine Kante geschnitten:
Abb. (*) (aus Rauber 1993)
Vorsicht beim Füllen von Polygonen, die sich über den Bildschirmrand erstrecken:
Wenn nur die sichtbaren Polygonkanten übergeben werden, kann z.B. für die Scangerade y =
a nicht entschieden werden, ob die entspr. Bildschirmzeile innerhalb oder außerhalb des Polygons liegt.Genaueres Vorgehen beim Einfärben:
naiver Algorithmus:
äußere Schleife: Scangerade verschieben
innere Schleife: Schnittpunkte mit allen Polygonkanten berechnen
effizienter:
äußere Schleife über die Polygonkanten e
innere Schleife: berechne für e alle Schnittpunkte mit allen Scangeraden zwischen
ymin(e) und ymax(e) und lege diese sortiert in einer
(globalen) Datenstruktur ab.
Danach durchlaufe die Datenstruktur und färbe die entspr. Segmente für jede Scangerade.
Vorteil: Bei Berechnung der Schnittpunkte kann ein inkrementeller Algorithmus verwendet werden
(Variante des Bresenham-Algorithmus).
In der Datenstruktur brauchen für jeden Schnittpunkt nur die x-Werte separat abgespeichert zu
werden, der y-Wert gilt für die gesamte Scangerade.
Dieses Beispiel bezieht sich auf obige Abb. (*):
Der Übersichtlichkeit halber sind hier noch die Labels der Punkte mit angegeben worden (a,b,c,d) - diese braucht man in der Praxis nicht. Fehler in der Abb.: ersetze das a bei der 4 durch c.
Diese Datenstruktur könnte in C etwa so realisiert werden:
Es gibt eine noch platzsparendere Variante, die einen bucket
sort-Algorithmus zur Erstellung einer Kantenliste ausnutzt (s. Foley et al. 1990).
Für beliebige Kurven als Grenzen der Füllregion muss nur die Berechnung der Schnittpunkte
geändert werden.
Die Saatfüll-Methode
wird benutzt, wo umgebende Polygone bereits auf dem Bildschirm dargestellt sind (interaktive Grafik)
keine geometrische Repräsentation des Polygons nötig!
stattdessen: Pixel der Grenzlinie müssen durch spezifische Grenzfarbe erkennbar sein.
Grundidee:
Setze ein Pixel im zu füllenden Bereich
setze dessen Nachbarpixel auf die Füllfarbe (sofern diese nicht die Grenzfarbe haben)
wiederhole dies, bis kein Pixel mehr neu gefärbt werden kann.
Beachte:
Die Nachbarschaft darf keine Diagonal-Pixel enthalten, da sonst vom Bresenham-Algorithmus gerasterte
Geraden "undicht" sind:
Implementierung:
naiver Ansatz: rekursiver Aufruf; ungünstig wegen Beanspruchung des Rekursionsstacks
effizienter:
"Horizontalisierung" des Füllens; 2 Schleifen: innere erzeugt "Pixelläufe" in horiz. Richtung,
äußere iteriert dies nach oben und unten.
Beachte: Auch hier ist (außer bei konvexen Polygonen) ein Stack erforderlich, der das Auftreten
zusätzlicher Pixelläufe verwaltet.
Beispiel:
(aus Rauber 1993)
Hier entstehen bei 1, 2, 3, 4 und 5 jeweils neue Pixelläufe.
Jeder Pixellauf wird durch sein rechtestes Pixel repräsentiert.
Der Algorithmus terminiert, wenn der Stack, der die Pixelläufe verwaltet, leer ist.
Nachteil:
Bei "engen" Polygonen kann der Saatfüll-Algorithmus vorzeitig stoppen. Abhilfe: Zweite Saat
setzen.
(aus Fellner 1992)
Clipping von Polygonen
häufige Aufgabe: Abschneiden von Geradensegmenten an einem Polygon (häufig an einem
Rechteck). Clipping, Clippen.
(Vgl. Clipping-Pfade in PostScript.)
einfachste Variante:
für jedes Pixel während der Bildschirm-Darstellung prüfen, ob es innerhalb des
Clipping-Polygons liegt.
Analytische Methode:
Schnittpunkte berechnen
Fallunterscheidungen schon bei einfachsten Objekten erforderlich.
Clippen eines Geradensegments s an einem rechteckigen Fenster
Grundsätzl. Vorgehensweise:
Endpunkte von s: P1 und P2
Parametergleichung der Geraden, auf der s liegt:
g = P1 + t(P2 - P1), t
beliebig
Für s selbst: t
Ziel: Zahl der Schnittpunktberechnungen vermindern!
Beispiel: Wenn beide Endpunkte von s links des Fensters liegen, kann s nicht durch das
Fenster verlaufen.
Ebenso: wenn beide rechts, beide oberhalb, beide unterhalb.
Systematisierung:
Algorithmus von Cohen und Sutherland
Kennzeichnung der 9 Bereiche in Bezug auf das Fenster durch 4 Bits. Bit 0 gesetzt für
Bereiche links des Fensters, 1 für rechts, 2 für unten, 3 für oben: ("outcodes")
Þ
Bitmuster von horizontal oder vertikal benachbarten Bereichen unterscheiden sich in genau 1 Bitposition.(aus Rauber 1993)
3 Beispielfälle für die Anwendung des Algorithmus
Programmskizze:
void CS_clip (float x1, float y1, float x2,
float y2, float xmin, float xmax,
float ymin, float ymax,
int value)
{
int c1, c2; float xs, ys;
c1 = code(x1, y1); c2 = code(x2, y2);
if (c1 | c2 == 0x0)
Draw_Line(x1, y1, x2, y2, value);
else
if (c1 & c2 != 0x0)
return;
else
{
intersect(xs, ys, x1, y1, x2, y2,
xmin, xmax, ymin, ymax);
if (is_outside(x1, y1))
CS_clip(xs, ys, x2, y2, xmin, xmax,
ymin, ymax, value);
else
CS_clip(x1, y1, xs, ys, xmin, xmax,
ymin, ymax, value);
}
}
intersect
berechnet in den Rückgabeparametern xs und ys einen Schnittpunkt zwischen dem Geradensegment und einer
der Fenstergeraden, die anhand der 4-Bit-Zahl der Endpunkte des Geradensegments ausgewählt wird.
is_outside berechnet,
ob der Punkt (x1,
y1) bzgl. derselben
Fenstergeraden außerhalb des Fensters liegt.
(nach Rauber 1993)
Clipping bzgl. beliebiger (auch konkaver) Polygone:
wichtig z.B. bei Systemen, die das Überlappen mehrerer Fenster auf einem Bildschirm erlauben!
Vorgehensweise:
Bestimme alle n Schnittpunkte des Geradensegments s mit dem Clipping-Polygon P.
Ordne diese nach aufsteigenden Parameterwerten.
Entscheide, welche von den n+1 Segmenten von s zwischen den Schnittpunkten im Inneren
von P liegen ("sichtbar sind"):
Liegt der Endpunkt P1 von s innerhalb von P?
Teststrahl von P1 aus, zähle Anzahl der Schnittpunkte mit P,
Paritätsprüfung (ungerade
(aus Rauber 1993).
Letzte Änderungen: 4. November 2001.