Wykład 2 Podstawowe algorytmy rysowania prymitywów 2D w
Transkrypt
Wykład 2 Podstawowe algorytmy rysowania prymitywów 2D w
Wykład 2
Podstawowe algorytmy rysowania prymitywów 2D w grafice rastrowej
Pakiet grafiki rastrowej aproksymuje prymitywy matematyczne (idealne), opisane przez
wierzchołki siatki kartezja skiej, za pomoc zbiorów pikseli o odpowiednim poziomie szaroci lub barwie. Piksele s pami tane w postaci mapy bitowej albo pikselowej w pami ci CPU
albo w pami ci obrazu. Implementacja pakietu graficznego wymaga konstrukcji algorytmów
rasteryzacji prymitywów.
Konwersja odcinków
Algorytm konwersji odcinka oblicza współrz dne pikseli, które le na lub blisko idealnej niesko czenie cienkiej linii prostej nało onej na siatk dwuwymiarowego rastra. Rysuj c
na urz dzeniu rastrowym przechodzimy do układu współrz dnych całkowitych (układ współrz dnych pikselowych). W takim układzie piksel b dzie reprezentowany jako kółko o rodku
w punkcie (x, y) w siatce całkowitoliczbowej (stosowane s równie inne reprezentacje pikseli). Rysuj c odcinek chcieliby my, eby sekwencja pikseli le ała tak blisko idealnego odcinka, jak to tylko mo liwe, i eby był on mo liwie prosty.
B dziemy rozwa a wy wietlanie odcinków o grubo ci jednego piksela, jednak pakiety
grafiki rastrowej powinny równie dawa mo liwo rysowania odcinków o grubo ci wi kszej ni 1 piksel.
Poni szy rysunek pokazuje (w du ym powi kszeniu) odcinek po konwersji (o grubo ci
1 piksela); wy wietlane piksele s zaznaczone jako niebieskie kółka; aproksymowany idealny
odcinek narysowany jest kolorem czerwonym
Je li zało ymy, e ko cami rysowanego odcinka s punkty o całkowitych współrz dnych (x0 , y0) i (xk , yk), to jako pierwszy piksel przybli aj cy rysowany odcinek przyjmujemy
oczywi cie jeden z kra cowych, np. (x0 , y0). Dalej mo emy stosowa wybór cztero- lub
o miokierunkowy (rysunek poni ej). Z czterech lub o miu mo liwych wybieramy ten piksel,
którego rodek le y najbli ej idealnego odcinka. W zale no ci od sposobu wyboru (czterolub o miokierunkowego) liczby wy wietlanych pikseli przybli aj cych ten sam odcinek b d
ró ne.
Sposób wyboru pikseli: czterokierunkowy i o miokierunkowy; niebieskie kółko oznacza piksel wybrany wcze niej:
1
Powy szy rysunek przedstawia zbiory pikseli przybli aj cych ten sam odcinek z wyborem
czterokierunkowym i o miokierunkowym. Oba rysunki robi wra enie „schodkowych”. Ten
niepo dany efekt jest wynikiem podej cia do procesu rasteryzacji na zasadzie wszystko albo
nic, zgodnie z któr barwa ka dego piksela jest albo zast powana przez barw prymitywu,
albo pozostaje bez zmiany. Zakłócenia tego typu s przykładem zjawiska okre lanego jako
aliasing. Metody redukcji albo eliminacji aliasingu s okre lane jako antyaliasing (odkłócanie).
Podstawowy algorytm przyrostowy
Naszym zadaniem jest wy wietlenie na monitorze rastrowym odcinka o ko cach
(x0 , y0) i (xk , yk), gdzie x0 , y0 , xk , yk s liczbami całkowitymi. Załó my ponadto, e nasz
odcinek ma nachylenie 0 < |m| ≤ 1 (rasteryzacja odcinków o innych nachyleniach wymaga
wprowadzenia drobnych zmian w naszych rozwa aniach; ponadto najcz ciej wyst puj ce
odcinki – poziome, pionowe, i o nachyleniu ±1 – mog by potraktowane jako specjalne
przypadki, poniewa przechodz wył cznie przez rodki pikseli). Bez zmniejszenia ogólno ci
mo emy zało y , e x0 < xk. Niech m = dy/dx, gdzie dy = yk – y0 , dy = xk – x0. Zaczynamy od
punktu poło onego z lewej strony tj. (x0 , y0). Zwi kszaj c warto ci x o 1 obliczamy
yi = mxi + b dla ka dego xi, gdzie xi = x0 ... xk (wzór y = mx + b okre la kierunkowe równanie
prostej zawieraj cej odcinek), nast pnie wy wietlamy piksel w punkcie (xi , Round( yi)), przy
czym Round( yi) = Floor( yi + 0,5). W wyniku tych oblicze wy wietlany jest piksel le cy
najbli ej rzeczywistego odcinka.
Nie jest to strategia efektywna, poniewa w ka dej iteracji wykonywane jest zmiennopozycyjne mno enie (przez m), dodawanie i wywołanie funkcji Round.
Zauwa my jednak, e
yi+1 = mxi+1 + b = m(xi +1)+ b = mxi + m + b = yi + m.
Zatem jednostkowej zmianie x towarzyszy zmiana y o m (nachylenie odcinka). Dla wszystkich punktów (xi , yi) na odcinku rzeczywistym (nie s to punkty otrzymywane w wyniku rasteryzacji odcinka) otrzymujemy wi c, xi+1 = xi+1 i yi+1 = yi + m. Oznacza to, e warto ci x
i y s zdefiniowane w zale no ci od poprzednich punktów. Na tym wła nie polega algorytm
przyrostowy – w ka dym kroku wykonujemy obliczenia przyrostowe korzystaj c z wyników
poprzedniego kroku.
odcinek rzeczywisty
Zauwa my, e w tej przyrostowej metodzie nie trzeba bezpo rednio zajmowa si współczynnikiem przesuni cia b wzdłu osi y.
Je eli |m| > 1, to krok w kierunku osi OX tworzy przyrost w kierunku osi OY wi kszy
ni 1. Musimy wi c zamieni x i y rolami, przypisuj c krok jednostkowy do y i zwi kszaj c
x o 1/m, tzn. xi+1 = xi+1/m.
2
Ten algorytm jest okre lany jako algorytm DDA (digital differential analyzer). Cho
cechuje si bardzo prost budow , to jego główn wad jest stosowanie arytmetyki na liczbach zmiennoprzecinkowych, co negatywnie wpływa na jego wydajno .
Załó my, e mamy dan funkcj niskiego poziomu wy wietlaj c piksel
PutPixel(int x, int y, int k), gdzie x,y s współrz dnymi a k warto ci piksela. Zdefiniujemy teraz funkcj Line rysuj c odcinek o danych ko cach:
void Line(int x0, int y0, int xk, int yk, int k)
{
int x;
float dx, dy, y, m;
dx = xk – x0;
dy = yk – y0;
m = dy/dx;
y = y0;
for(x = x0; x <= xk; x++)
{
PutPixel(x,(int)floor(y + 0.5),k);
y += m;
}
}
W definicji powy szej funkcji zało yli my, e –1 ≤ m ≤ 1 oraz x0 < xk.
wiczenie 1.
Uogólni funkcj Line, tak aby rysowała odcinki bez adnych dodatkowych zało e , tzn.
gdy
x0 < xk, x0 > xk, x0 = xk,
|m| ≤ 1, |m| > 1.
Ponadto zadba o to, eby odcinek rysowany od (x0,y0) do (xk,yk) zawierał ten sam zestaw pikseli co odcinek rysowany od (xk,yk) do (x0,y0), tzn.
Line(x0,y0,xk,yk,k) “=” Line(xk,yk,x0,y0,k).
Algorytm Bresenhama konwersji odcinka (1963)
Wad funkcji Line jest to, e zaokr glanie y do warto ci całkowitej zajmuje czas i e
zmienne y i m musz by rzeczywiste albo ułamkowe, poniewa nachylenie jest ułamkiem.
Bresenham opracował algorytm, który jest atrakcyjny z tego wzgl du, e korzysta tylko z
arytmetyki liczb całkowitych, dzi ki czemu unika si funkcji Round. Algorytm ten umo liwia
przyrostowe obliczanie (xi+1, yi+1), tzn. z wykorzystaniem wykonanych ju oblicze dla
(xi, yi). Zmiennopozycyjna wersja tego algorytmu mo e by zastosowana do odcinków
o dowolnych rzeczywistych warto ciach współrz dnych punktów ko cowych.
Tak jak poprzednio, naszym zadaniem b dzie wy wietlenie odcinka o danych ko cach
(x0 , y0) i (xk , yk). Zakładamy, e 0 < m ≤ 1 (nachylenie odcinka); w przypadku innych nachyle mo na skorzysta z odpowiednich symetrii wzgl dem podstawowych osi lub zmieni o
wiod c (zamieni x i y rolami). Przyjmujemy, e lewy dolny koniec odcinka ma współrz dne
P0 = (x0 , y0), a prawy górny koniec odcinka ma współrz dne Pk = (xk , yk). Wtedy m = dy/dx,
gdzie dx = xk – x0, dy = yk –y0.
3
Poniewa k t nachylenia odcinka jest ograniczony do przedziału (0°, 45°], wiec po znalezieniu punktu Pi = (xi , yi) nast pny piksel wybieramy tylko spo ród dwóch A = (xi+1 , yi),
B = (xi+1 , yi+1). Niech Q b dzie punktem rzeczywistego odcinka o odci tej xi+1. Niech
a = (Q, A) oraz b = (Q, B), gdzie oznacza odległo mi dzy punktami. Wówczas
a = (Q, A) = dy/dx(xi+1 – x0) – ( yi – y0)
b = (Q, B) = ( yi +1 – y0) – dy/dx(xi+1 – x0)
Odejmuj c powy sze równania stronami otrzymujemy
a – b = 2dy/dx(xi+1 – x0) – ( yi – y0) – ( yi +1 – y0).
Mno c powy sze równanie przez dx otrzymujemy
dx(a – b) = 2dy(xi – x0) – 2dx( yi – y0) +2dy – dx.
Oznaczmy di = dx(a – b). Zatem
(1)
di = 2dy(xi – x0) – 2dx( yi – y0) +2dy – dx.
Poniewa dx > 0, wiec znak di okre la która z wielko ci a i b jest wi ksza.
Je li di > 0, to a > b i nast pny piksel Pi+1 = B, a je eli di ≤ 0, to b ≥ a i Pi+1 = A.
(Je li di = 0, to mo emy wybra dowolny punkt.)
Dla i+1 otrzymujemy (na podstawie (1))
di+1 = 2dy(xi+1 – x0) – 2dx( yi+1 – y0) +2dy – dx.
Obliczymy teraz ró nic di+1 – di
di+1 – di = 2dy(xi+1 – xi) – 2dx( yi+1 – yi).
Zatem, zauwa aj c, e xi+1 – xi =1, otrzymujemy
di+1 = di + 2dy – 2dx( yi+1 – yi).
Je li di > 0 (Pi+1 = B), to yi+1 = yi+1. I wtedy
di+1 = di + 2(dy – dx)
Je li di ≤ 0 (Pi+1 = A), to yi+1 = yi. I wtedy
di+1 = di + 2dy
4
Z uwagi na rekurencyjn posta wzoru wyznaczaj cego warto
pocz tkow d0. Na podstawie (1) dla i = 0 mamy
di nale y wyliczy warto
d0 = 2dy(x0 – x0) – 2dx( y0 – y0) +2dy – dx
Mamy zatem warunek pocz tkowy:
d0 = 2dy – dx
Widzimy, e o wyborze kolejnego piksela decyduje znak zmiennej di, zmienn t nazywamy
zmienna decyzyjn .
Przy przyj tych zało eniach mo emy teraz zdefiniowa funkcj BLine rysuj c odcinek o danych ko cach według algorytmu Bresenhama:
void BLine(int x0, int y0, int xk, int yk, int k)
{
int dx, dy, d, dA, dB, x, y;
dx = xk – x0;
d = 2*dy – dx; //warto
pocz tkowa zmiennej decyzyjnej d
dA = 2*dy;
dB = 2*(dy – dx);
x = x0;
y = y0;
PutPixel(x,y,k);
While (x < xk)
{
if (d < 0) //wy wietlamy piksel A
{
d += dA;
x++;
}
else
//wy wietlamy piksel B
{
d += dB;
x++;
y++;
}
PutPixel(x,y,k);
}
}
W definicji powy szej funkcji, w ka dym kroku obliczenia potrzebne do wyznaczenia nowej
warto ci zmiennej decyzyjnej d sprowadzaj si do prostego dodawania całkowitoliczbowego. W ogóle nie ma czasochłonnego mno enia. Ta wersja algorytmu działa tylko dla odcinków o nachyleniu z przedziału (0, 1]. Implementacja algorytmu Bresenhama musi oczywi cie
uwzgl dnia inne mo liwe poło enia odcinka wzgl dem osi OX. Jednak w ka dej sytuacji
mo na zastosowa opisany wy ej schemat, w razie potrzeby traktuj c o OY jako o wiod c .
Algorytm Bresenhama mo na przy pieszy np. poprzez podział odcinka na kilka cz ci i odpowiednie wykorzystanie symetrii.
5
wiczenie 2.
Uogólni funkcj BLine, tak eby rysowała odcinki bez adnych dodatkowych zało e (tak
jak w wiczeniu 1).
wiczenie 3.
Opracowa ulepszon wersj algorytmu rysowania odcinka (korzystaj c tylko z arytmetyki
całkowitoliczbowej), który w kolejnym kroku zamiast jednego wybiera jednocze nie dwa
kolejne piksele.
B dzie to algorytm z podwójnym krokiem; liczba decyzji (wyboru nast pnych pikseli) zostanie zredukowana o połow .
Wskazówka:
Zauwa my, e mog wyst pi nast puj ce cztery kombinacje pikseli:
Mo na wykaza , e wzory 1 i 4 nie mog jednocze nie wyst pi . Ponadto, je li nachylenie
odcinka jest wi ksze ni ½, to nie mo e wyst pi wzór 1; podobnie, je li nachylenie odcinka
jest mniejsze ni ½, to nie mo e wyst pi wzór 4. Zatem testowanie nachylenia odcinka b dzie ogranicza wybór do jednego z trzech wzorów: 1, 2, 3 albo 2, 3, 4.
Problemy przy konwersji odcinków
Kolejno punktów ko cowych
Funkcje realizuj ce konwersj odcinków powinny zapewnia , eby odcinek rysowany od
punktu P0 do punktu Pk zawierał ten sam zestaw pikseli co odcinek rysowany od Pk do
P0. Czyli obraz otrzymanego odcinka nie powinien zale e od kolejno ci podawania
punktów ko cowych.
Proste przeł czenie ko ców odcinka nie sprawdza si je eli korzystamy ze stylów linii.
Styl odcinka zawsze zaczepia okre lon mask zapisu w punkcie pocz tkowym. Nie osi gniemy po danego efektu wizualnego, je li wzór nie b dzie zaczynał si tam, gdzie zostanie okre lony punkt pocz tkowy, ale automatycznie w lewym dolnym ko cu odcinka.
Przykład odcinków rysowanych tym samym stylem linii:
Zmiana jasno ci odcinka
odcinek B
odcinek A
Przyjrzyjmy si dwóm odcinkom po konwersji na powy szym rysunku. Odcinek B le cy
na przek tnej siatki ma nachylenie 1 i jest 2 razy dłu szy od poziomego odcinka A,
chocia do narysowania obydwóch odcinków została wykorzystana taka sama liczba pik6
seli. Je eli jasno ka dego piksela wynosi J, to jasno na jednostk długo ci dla odcinka
A jest równa J, natomiast dla odcinka B tylko J / 2 ; ta ró nica jest łatwo zauwa ana
przez obserwatora. Na monitorze monochromatycznym nie mo na tego problemu rozwi za , natomiast w systemie z n bitami mo emy wprowadzi kompensacj uzale niaj c jasno pikseli od nachylenia odcinka.
Rysowanie prymitywów konturowych budowanych z odcinków
Je li mamy zdefiniowan funkcj rysuj c odcinki, to mo emy równie dokonywa konwersji prymitywów budowanych z odcinków. Konwersj łamanej, prostok tów i wielok tów mo na wykona na zasadzie kolejnego rozpatrywania odcinków. Nale y jednak
uwa a , eby rysowa wierzchołki tylko raz, poniewa dwukrotne narysowanie tego
samego wierzchołka mo e powodowa zmian barwy lub pojawienie si barwy tła (w
zale no ci trybu zapisu na ekranie), albo podwojenie jasno ci piksela.
Konwersja okr gów
Przy rysowaniu okr gu korzysta si z kołowego łuku eliptycznego jako specjalnego
przypadku oraz z o miokrotnej symetrii.
Załó my, e promie R okr gu jest liczb naturaln , a jego rodek le y w pocz tku układu
współrz dnych (ewentualnie wykonujemy przesuni cie). Nasze zadanie b dzie polega na
wybraniu pikseli przybli aj cych krzyw dan równaniem
x2 + y2 – R2 =0
lub inaczej
F(x, y) = x2 + y2 – R2 =0.
Ze wzgl du na symetri mo emy ograniczy si do pierwszej wiartki okr gu, a nawet rozwa y tylko 1/8 okr gu. Dla pełnego okre lenia okr gu trzeba wykona obliczenia tylko dla
segmentu o k cie 45°.
(-x, y)
(x, y)
(-y, x)
(y, x)
R/
(-y, -x)
(-x, -y)
2
(y, -x)
(x, -y)
Je eli punkt (x, y) nale y do okr gu to w trywialny sposób mo emy obliczy siedem pozostałych punktów okr gu (rysunek powy ej). Dla okr gu o rodku w pocz tku układu współrz dnych zdefiniujmy funkcj Sym8, która b dzie wy wietla osiem symetrycznych punktów
(funkcj mo na łatwo uogólni na przypadek okr gu o rodku le cym w dowolnym punkcie):
7
void Sym8 (int x, int y, int k)
{
PutPixel(x,y,k);
PutPixel(y,x,k);
PutPixel(y,-x,k);
PutPixel(x,-y,k);
PutPixel(-x,-y,k);
PutPixel(-y,-x,k);
PutPixel(-y,x,k);
PutPixel(-x,y,k);
}
Łatwo mo na zmodyfikowa kod powy szej funkcji tak, aby dla |x| = |y| lub x = 0 lub y = 0
piksele nie były rysowane podwójnie.
Algorytm Bresenhama konwersji okr gu (1983)
Algorytm ten jest tak e nazywany kryterium punktu rodkowego.
Rozwa amy jedynie 45° drugiego oktantu okr gu, od x = 0 do x = y = R / 2 , i u ywamy funkcji Sym8 do wy wietlenia punktów na całym okr gu.
Dla ka dego x wybieramy punkt, który le y bli ej rzeczywistego okr gu.
Je li piksel Pi = (xi , yi) został poprzednio wybrany jako bli szy okr gowi, to jako nast pny
mo e zosta wybrany piksel A lub B.
Funkcja F(x, y) = x2 + y2 – R2 jest równa 0 na okr gu, dodatnia na zewn trz okr gu i ujemna
wewn trz okr gu. Je eli punkt rodkowy Mi+1 le y na zewn trz okr gu, to piksel B le y bli ej
okr gu, a je li punkt Mi+1 le y wewn trz okr gu, to piksel A le y bli ej okr gu.
Podobnie jak dla odcinków, wyboru b dziemy dokonywa na podstawie zmiennej
decyzyjnej d, która w tym przypadku b dzie warto ci funkcji F w punkcie rodkowym. Obliczmy
di+1 = F(Mi+1) = F(xi + 1, yi – ½) = (xi + 1)2 + (yi – ½)2 – R2.
Je li di+1 < 0, to Pi+1 = A, i wtedy Mi+2 = (xi + 2, yi – ½). Zatem
di+2 = F(Mi+2) = F(xi + 2, yi – ½) = (xi + 2)2 + (yi – ½)2 – R2.
Mo emy zatem wyznaczy di+2 w zale no ci od di+1:
di+2 = di+1 + (2xi + 3)
Oznaczmy przez dA przyrost: dA = 2xi + 3.
Je li di+1 ≥ 0, to Pi+1 = B, i wtedy Mi+2 = (xi + 2, yi – 3/2). Zatem mo emy obliczy
8
di+2 = F(Mi+2) = F(xi + 2, yi – 3/2) = (xi + 2)2 + (yi – 3/2)2 – R2
Wyznaczamy di+2 w zale no ci od di+1:
di+2 = di+1 + (2xi – 2 yi + 5)
Oznaczmy przez dB przyrost: dB = 2(xi – yi ) + 5.
Zauwa my, e dA i dB s funkcjami konkretnych warto ci (nie s stałe).
Z uwagi na rekurencyjn posta wzoru wyznaczaj cego warto di nale y wyliczy warto
pocz tkow d1. Poniewa P0 = (0, R), wi c M1 = (0, R – ½). Mamy zatem
d1 = F(M1) = F(1,R – ½) = 1 + (R2 – R +1/4) = 5/4 –R.
Mamy zatem warunek pocz tkowy:
d1 = 5/4 – R
Przy przyj tych zało eniach mo emy teraz zdefiniowa funkcj Circle rysuj c
okr g o danym promieniu według algorytmu Bresenhama (wykorzystamy wcze niej zdefiniowan funkcj Sym8):
void Circle(int r, int k)
{
int x, y;
float d;
x = 0;
//współrz dne punktu P0
y = r;
d = 5/4 – r; //warto
pocz tkowa zmiennej decyzyjnej d
Sym8(x,y,k);
While (y < x)
{
if (d < 0) //wy wietlamy piksel A
{
d = d + 2*x + 3;
x++;
}
else
//wy wietlamy piksel B
{
d = d + 2*(x – y) + 5;
x++;
y--;
}
Sym8(x,y,k);
}
}
Problem z powy sz wersj algorytmu polega na tym, e musimy korzysta z arytmetyki
zmiennopozycyjnej ze wzgl du na ułamkow inicjalizacj d. Chocia funkcja mo e by łatwo
zmodyfikowana tak, eby mo na było rysowa okr gi o rodkach o współrz dnych, które nie
9
s całkowite albo których promie nie jest całkowity, to jednak chcieliby my otrzyma efektywniejsz wersj – w pełni całkowitoliczbow . W celu wyeliminowania ułamków dokonamy
drobnej modyfikacji w kodzie funkcji. Definiujemy now zmienn decyzyjn h jako h = d –¼
i podstawiamy w kodzie h + ¼ zamiast d. Przy inicjalizacji b dziemy mie h = 1 – r i porównanie d < 0 zast pimy przez h < –¼. Jednak, poniewa h jest całkowite i jest zwi kszane o
warto ci całkowite, wi c mo emy zmieni porównanie na h < 0. Mamy teraz całkowitoliczbowy kod funkcji (dla zgodno ci z poprzednia funkcj podstawimy d zamiast h):
void Circle(int r, int k)
{
int x, y, d;
x = 0;
y = r;
// warto ci poczatkowe
d = 1 – r;
Sym8(x,y,k);
While (y < x)
{
if (d < 0) //wy wietlamy piksel A
{
d = d + 2*x + 3;
x++;
}
else
//wy wietlamy piksel B
{
d = d + 2*(x – y) + 5;
x++;
y--;
}
Sym8(x,y,k);
}
}
wiczenie 4.
Uogólni algorytm rysowania okr gów dla okr gów o dowolnym rodku (zmodyfikowa
funkcj Sym8).
wiczenie 5.
Opracowa algorytm rysowania elipsy.
Wskazówki:
Elipsa mo e by dana równaniem F(x, y) =b2x2 + a2y2 – a2b2 = 0, gdzie a i b s półosiami
elipsy. W tym przypadku musimy rysowa cał pierwsz wiartk elipsy oraz ustali jako o
wiod c o OX gdy x zmienia si szybciej ni y, natomiast o OY, gdy y zmienia si szybciej
ni x. Do ka dego wyznaczonego piksela stosujemy funkcj rysuj c cztery symetryczne
punkty.
wiczenie 6.
Opracowa algorytm rysowania łuku okr gu (łuku elipsy).
wiczenie 7.
Opracowa algorytm konwersji odcinków korzystaj c z kryterium punktu rodkowego.
Tutaj prosta zawieraj ca odcinek mo e by zadana równaniem: F(x, y) = xdy – ydx + bdx = 0.
10