Grafikprogrammierung
2D Objekte: Rasterkonvertierung
Thorsten Thormählen
21. Oktober 2024
Teil 3, Kapitel 2
Thorsten Thormählen
21. Oktober 2024
Teil 3, Kapitel 2
Dies ist die Druck-Ansicht.
Weiterschalten der Folien durch die → Taste oder
durch das Klicken auf den rechten Folienrand.
Das Weiterschalten der Folien kann ebenfalls durch das Klicken auf den rechten bzw. linken Folienrand erfolgen.
Typ | Schriftart | Beispiele |
---|---|---|
Variablen (Skalare) | kursiv | $a, b, x, y$ |
Funktionen | aufrecht | $\mathrm{f}, \mathrm{g}(x), \mathrm{max}(x)$ |
Vektoren | fett, Elemente zeilenweise | $\mathbf{a}, \mathbf{b}= \begin{pmatrix}x\\y\end{pmatrix} = (x, y)^\top,$ $\mathbf{B}=(x, y, z)^\top$ |
Matrizen | Schreibmaschine | $\mathtt{A}, \mathtt{B}= \begin{bmatrix}a & b\\c & d\end{bmatrix}$ |
Mengen | kalligrafisch | $\mathcal{A}, B=\{a, b\}, b \in \mathcal{B}$ |
Zahlenbereiche, Koordinatenräume | doppelt gestrichen | $\mathbb{N}, \mathbb{Z}, \mathbb{R}^2, \mathbb{R}^3$ |
gegeben: $\mathbf{p}_0 = (x_0, y_0)^\top, \mathbf{p}_1 = (x_1, y_1)^\top$
gesucht: Geradengleichung $\mathrm{g}(\mathbf{p})$ mit $\mathbf{p}= (x, y)^\top$
aus Zeichung: $\frac{y-y_0}{x-x_0} = \frac{y_1-y_0}{x_1-x_0}$
\begin{aligned} \frac{y-y_0}{x-x_0} &= \frac{y_1-y_0}{x_1-x_0} \\ y-y_0 &= \underbrace{\frac{y_1-y_0}{x_1-x_0}}_{m} \, \, (x-x_0) \\ y&= m \, (x - x_0) + y_0\\ y &= m \, x \, \underbrace{- \, m \, x_0 + y_0}_{b}\\ y &= m \, x + b\\ \end{aligned}
void drawLine(int x0, int y0, int x1, int y1) { int dx = x1 - x0; int dy = y1 - y0; float m = dy / dx; for(int x = x0; x <= x1; x++) { float y = m * (x - x0) + y0; setPixel(x, round(y)); } } void drawLineFloat(float x0, float y0, float x1, float y1) { drawLine(round(x0), round(y0), round(x1), round(y1)) ; }
Problem 1: Funktioniert nur für $x_1 > x_0$
Lösung: Punkte tauschen, wenn $x_1 < x_0$, einzelnes Pixel zeichnen wenn $x_1 = x_0$.
Problem 2: Lücken in der Linie, wenn Betrag von $\Delta_y = y_1-y_0$ größer als Betrag von $\Delta_x = x_1-x_0$
Lösung: Die Lücken entstehen, da die Schleife über $x$ läuft. Daher Schleife über $y$, wenn $|\Delta_y|>|\Delta_x|$
drawLine(int x0, int y0, int x1, int y1) { int dy = y1 - y0; int dx = x1 - x0; if(dx == 0 && dy ==0) { setPixel(x0, y0); return;} if(abs(dx) >= abs(dy)) { if (x1 > x0) drawLineX(x0, y0, x1, y1); else drawLineX(x1, y1, x0, y0); }else{ if (y1 > y0) drawLineY(x0, y0, x1, y1); else drawLineY(x1, y1, x0, y0); } } void drawLineX(int x0, int y0, int x1, int y1) { int dy = y1 - y0; int dx = x1 - x0; float m = dy / dx; for(int x = x0; x <= x1; x++) { float y = m * (x - x0) + y0; setPixel(x, round(y)); } } void drawLineY(int x0, int y0, int x1, int y1) { int dy = y1 - y0; int dx = x1 - x0; float mInv = dx / dy; for(int y = y0; y <= y1; y++) { float x = mInv * (y - y0) + x0; setPixel(round(x), y); } }
\begin{aligned} y_{i+1} &= m \, x_{i+1} + b\\ y_{i+1} &= m \, \left(x_i + (x_{i+1}-x_i)\right) +b \\ y_{i+1} &= y_i + m \,(x_{i+1}-x_i)\\ y_{i+1} &= y_i + m\\ \end{aligned}
void drawLineXFaster(int x0, int y0, int x1, int y1) { int dy = y1 - y0; int dx = x1 - x0; float m = dy / dx; float y = y0; for(int x = x0; x <= x1; x++) { setPixel(x, round(y)); y += m; } } void drawLineYFaster(int x0, int y0, int x1, int y1) { int dy = y1 - y0; int dx = x1 - x0; float mInv = dx / dy; float x = x0; for(int y = y0; y <= y1; y++) { setPixel(round(x), y); x += mInv; } }
Beobachtung von Bresenham (1965):
Wenn die Schleife in $x$-Richtung läuft und ein Pixel gezeichnet wurde, gibt es nur zwei Optionen für das
nächsten Pixel:
Die Entscheidung, welcher Fall vorliegt, kann durch Betrachtung des Mittelpunkts $(x+1, y+0.5)$ getroffen
\begin{aligned} \frac{y-y_0}{x-x_0} &= \frac{y_1-y_0}{x_1-x_0} \\ (y-y_0)\, \underbrace{(x_1-x_0)}_{\Delta_x}&= \underbrace{(y_1-y_0)}_{\Delta_y}\, \, (x-x_0) \\ (y-y_0)\, \Delta_x&= \Delta_y\, \, (x-x_0) \\ 0 &= \Delta_y \,x - \Delta_x \, y - \Delta_y \, x_0 + \Delta_x \, y_0 \\ \mathrm{g}(x,y) &= \underbrace{\Delta_y}_{a} \,x \, \underbrace{- \Delta_x}_{b} \, y + \underbrace{\Delta_x \, y_0 - \Delta_y \, x_0 }_{c} \\ \mathrm{g}(x,y) &= a x + b y + c \end{aligned}
Implizite Geradengleichung:
\begin{aligned} \mathrm{g}(x,y) &= a x + b y + c \\ \end{aligned}
Mittelpunktalgorithmus:
Inkrementelle Berechnung der Entscheidungsvariablen $d_{i} = \mathrm{g}(x+1, y+0.5)$:
\begin{aligned} \mathrm{g}(x,y) &= \Delta_y \,x - \Delta_x \, y \, + \underbrace{\Delta_x \, y_0 - \Delta_y \, x_0 }_{c} \\ \mathrm{g}(x+1, y+0.5) &= \Delta_y \,(x+1) - \Delta_x \, (y+0.5) + c \end{aligned}
Wenn keine Erhöhung:
\begin{aligned} \mathrm{g}(x+2, y+0.5) &= \Delta_y \,(x+2) - \Delta_x \, (y+0.5) + c \\ &= \mathrm{g}(x+1, y+0.5) + \Delta_y \\ d_{i+1} &= d_i + \Delta_y \end{aligned}
Wenn Erhöhung um 1 :
\begin{aligned} \mathrm{g}(x+2, y+1.5) &= \Delta_y \,(x+2) - \Delta_x \, (y+1.5) + c \\ &= \mathrm{g}(x+1, y+0.5) + \Delta_y - \Delta_x\\ d_{i+1} &= d_i + \Delta_y - \Delta_x \end{aligned}
Zusätzlich wird noch der initiale Wert $d_0$ der Entscheidungsvariablen benötigt:
\begin{aligned} d_0 &= \mathrm{g}(x_0+1, y_0+0.5)\\ &= \Delta_y \,(x_0+1) - \Delta_x \, (y_0+0.5) + c\\ &= \Delta_y \,x_0 + \Delta_y - \Delta_x \, y_0 - 0.5 \Delta_x + c\\ &= \Delta_y \,x_0 - \Delta_x \, y_0 + \Delta_y - 0.5 \Delta_x + c\\ &= \underbrace{\mathrm{g}(x_0, y_0)}_{=0} + \Delta_y - 0.5 \Delta_x \\ &= \Delta_y - 0.5 \Delta_x \\ \end{aligned}
void drawLineXEvenFaster(int x0, int y0, int x1, int y1) { int dx = x1 - x0; int dy = abs(y1 - y0); int twoDx = dx + dx; int twoDy = dy + dy; int dHat = twoDy - dx; // init decision variable int incr = 1; if (y1 < y0) incr = -1; // increase or decrease y for(int x = x0, y = y0; x <= x1; x++) { setPixel(x, y); if(dHat <= 0) { // no increment dHat += twoDy; } else { // increment dHat += twoDy - twoDx; y += incr; } } } void drawLineYEvenFaster(int x0, int y0, int x1, int y1) { int dx = abs(x1 - x0); int dy = y1 - y0; int twoDx = dx + dx; int twoDy = dy + dy; int dHat = twoDx - dy; // init decision variable int incr = 1; if (x1 < x0) incr = -1; // increase or decrease y for(int y = y0, x = x0; y <= y1; y++) { setPixel(x, y); if(dHat <= 0) { // no increment dHat += twoDx; } else { // increment dHat += twoDx - twoDy; x += incr; } } }
Implizite Gleichung für einen Kreis mit Radius $r$:
\begin{aligned} \mathrm{g}(x,y) &= x^2 + y^2 - r^2 \\ \end{aligned}
Mittelpunktalgorithmus:
Inkrementelle Berechung der Entscheidungsvariablen $d_{i} = \mathrm{g}(x+1, y-0.5)$:
\begin{aligned} \mathrm{g}(x,y) &= x^2 + y^2 - r^2 \\ \mathrm{g}(x+1, y-0.5) &= (x+1)^2 + (y-0.5)^2 - r^2 = d_{i} \end{aligned}
Wenn keine Reduktion:
\begin{aligned} \mathrm{g}(x+2, y-0.5) &= (x+2)^2 + (y-0.5)^2 - r^2\\ &= (x+2)^2 + d_i - (x+1)^2\\ &= (x^2+4x+2^2) + d_i - (x^2+2x+1)\\ d_{i+1} &= d_i + 2x+ 3 \\ \end{aligned}
Wenn Reduktion um 1:
\begin{aligned} \mathrm{g}(x+2, y-1.5) &= (x+2)^2 + (y-1.5)^2 - r^2 \\ &= (x+2)^2 + (y-1.5)^2 + d_i - (x+1)^2 - (y-0.5)^2\\ d_{i+1} &= d_i + 2x - 2y + 5\\ \end{aligned}
Zusätzlich wird noch der initiale Wert $d_0$ der Entscheidungsvariablen benötigt:
\begin{aligned} d_0 &= \mathrm{g}(x_0+1, y_0 + r-0.5)\\ &= \mathrm{g}(1, r-0.5)\\ &= (1)^2 + (r-0.5)^2 - r^2\\ &= 1 + r^2 -r + 0.25 - r^2\\ &= 1.25 - r \\ &= \frac{5}{4} - r\\ \end{aligned}
function drawCircle(int x0, int y0, int radius) { int dHat = 5 - 4 * radius; // init decision variable int x = 0; int y = radius; for(int x = 0; x <= y ; x++) { setSymmetricPixelCirle(x, y, x0, y0); if(dHat <= 0) { // no reduction of y dHat += 8*x + 12; }else { // reduction of y by 1 dHat += 8*x - 8*y + 20; y--; } } }
void setSymmetricPixelCirle(int x, int y, int x0, int y0) { setPixel( x+x0, y+y0); setPixel(-x+x0, y+y0); setPixel( x+x0, -y+y0); setPixel(-x+x0, -y+y0); setPixel( y+x0, x+y0); setPixel(-y+x0, x+y0); setPixel( y+x0, -x+y0); setPixel(-y+x0, -x+y0); }
Der Scanline-Algorithmus kann sehr effizient implementiert werden durch:
Dazu wird benötigt:
class Edge { int yMin; // smallest value of y (when edge enters) int yMax; // largest value of y (when edge leaves) float xHit; // intersection point (init with x value at yMin) float mInv; // dx/dy (inverse line increment) }
Ein interaktiver Demonstrator zur Visualisierung des Scanline-Algorithmus steht auf der Webseite zur Vorlesung zur Verfügung
Ein interaktiver Demonstrator zur Visualisierung des Pineda-Algorithmus steht auf der Webseite zur Vorlesung zur Verfügung
Anregungen oder Verbesserungsvorschläge können auch gerne per E-mail an mich gesendet werden: Kontakt