Tietokonegrafiikan perusteet T-111.4300 3 op Luento 3: Tulostusprimitiivien toteutus Lauri Savioja 11/05 Primitiivien toteutus / 1 Suora ja ympyrä Antialiasointi Fill-algoritmit Point-in-polygon Sisältö Primitiivien toteutus / 2 1
GRAAFISTEN PRIMITIIVIEN TOTEUTUS HUOM! Oletuksena on XY-koordinaatisto Suorien viivojen piirtäminen Malli piirturikynällä piirtämisestä Kynä on aina jossakin (CP = Current Position) Inkrementaaliset alkeisoperaatiot: PenUp / PenDown CP' = CP + (±1, ±1)-> 8 eri askelta Primitiivien toteutus / 3 Piirtäminen rasteriruudulle Alkeisoperaationa: SetPixel ( x, y, color ) Pisteet piirrettävissä missä järjestyksessä hyvänsä Koordinaatit suhteellisina määritellyn rasterin origoon nähden Primitiivien toteutus / 4 2
Laskennallisia vaatimuksia Nopea, eli kokonaislukuja yhteen- ja vähennyslaskuja shiftejä Hitaita asioita kertolaskut reaaliluvut kirjastofunktiot, esim. trigonometriset operaatiot Primitiivien toteutus / 5 Suoran viivan vaatimukset Suora Tasapaksu (tasainen intensiteetti) Portaaton Täsmällinen alku ja loppu Intensiteetti riippumaton pituudesta ja kulmakertoimesta Primitiivien toteutus / 6 3
Algoritmi suoran piirtämiseksi Suoran yhtälö y=ax+b Alkupiste (x o,y o ), loppupiste (x 1,y 1 ) Primitiivien toteutus / 7 DDA (Digital Differential Analyzer) - algoritmi Periaate: pisteitä suoralla voidaan laskea esim. janan päätepisteiden (p 1, p 2 ) avulla esitetystä parametrimuodosta p(t) = p 1 + t * p = p 1 + t * (p 2 - p 1 ) ; t [0,1] x(t) = x 1 + t * X y(t) = y 1 + t * Y = y 1 + m * (t * X) ; m = Y / X eli antamalla t:lle sopivia arvoja ja pyöristämällä lasketut koordinaatit. Primitiivien toteutus / 8 4
DDA (jatkuu) symmetrinen DDA : iteroidaan t-parametria riittävän pienin askelin, jotta kaikki "janalla olevat" (so. lähellä olevat) pisteet tulevat käytyä läpi. yksinkertainen DDA : iteroidaan yksikköaskelin sitä koordinaattia, jonka siirtymä on suurempi, esim. x increment = ±1 ja y increment = m, kun m < 1. heikkouksia: jakolasku alussa liukuluvut, pyöristys joka pisteessä Primitiivien toteutus / 9 Bresenham PASCAL-kielisenä: procedure bres_line (x1, y1, x2, y2 : integer); var dx, dy, x, y, x_end, p, const1,const2 : integer; begin dx := abs(x1 - x2); dy := abs(y1 - y2); p := 2 * dy - dx; const1 := 2 * dy; const2 := 2 * (dy - dx); if x1 > x2 then begin { if x1 > x2 } x := x2; y := y2; x_end := x1 end else begin { if x1 <= x2 } x := x1; y := y1; x_end := x2 end; set_pixel(x, y); while x < x_end do begin x := x + 1; if p < 0 then p := p + const1 else begin y := y + 1; p := p + const2 end; set_pixel(x, y) end end; Primitiivien toteutus / 10 5
Bresenham (jatkuu) algoritmin edut: ei lainkaan jakolaskua eikä liukulukuja kertolasku vain tekijällä 2 (= bin.luvun sivuttaissiirto) Huom. algoritmi johdettu nousevalle suoralle, dx>0, m < 1. Jos dx < 0, vaihdetaan päätepisteet (p 1, p 2 ) keskenään, tai päivitetään x-arvoa -1:n askelin laskevalle suoralle, -1 < m < 0, vaihdetaan y:n askeleeksi -1 Jos m > 1, vaihdetaan x:n ja y:n roolit keskenään. Primitiivien toteutus / 11 KÄYRÄT VIIVAT suorien ohella tarvitaan usein myös käyriä: 2 käyrät (ympyrän, ellipsin, parabelin kaaret) splinit (myöhemmin tällä kurssilla) periaatteessa mikä hyvänsä käyrä voidaan approksimoida murtoviivalla, mutta tehokkaampaa on yleensä tuottaa käyrän pisteet suoraan sen määritelmästä DDA-algoritmi ja Bresenhamin muunnelma siitä ovat helposti muunnettavissa monille käyrille sopiviksi, mutta on varottava äärellisestä differenssiaskeleesta aiheutuvaa virhettä. Primitiivien toteutus / 12 6
Bresenhamin ympyränpiirtoalgoritmi symmetrian ansiosta riittää tarkastella vain kahdeksasosaa koko ympyrästä; loput pisteet saadaan vaihtamalla x:n ja y:n rooleja ja etumerkkejä (olettaen, että keskipiste on origossa): -x,y x,y -y,x y,x -y,-x y,-x -x,-y x,-y piirtäminen alkaa pisteestä (0,r) ja päättyy, kun x=y. Primitiivien toteutus / 13 Ympyrä (jatkuu) Tarkastellaan askelta i, jossa ollaan viimeksi piirretty piste (x i, y i ), ja seuraavaksi on pääteltävä kumpi pisteistä, (x i +1, y i ) vai (x i +1, y i 1), on lähempänä todellista käyrän pistettä (x i +1, y exact ) : y exact yi y i-1 d1 d2 xi xi+1 Primitiivien toteutus / 14 7
Ympyrä (jatkuu) ympyrän yhtälöstä y 2 = r 2 x 2 saadaan: y exact 2 = r 2 (x i + 1) 2, jota voidaan verrata vaihtoehtoisiin pisteisiin: d 1 = y i 2 y exact 2 = y i 2 r 2 + (x i + 1) 2, d 2 = y exact 2 (y i - 1) 2 = r 2 (x i + 1) 2 (y i - 1) 2 p i = d 1 d 2 = 2(x i + 1) 2 + y i 2 + (y i - 1) 2 2r 2 valitaan se piste, jonka etäisyys oikeasta on pienempi, eli jos parametri p i < 0, niin valitaan arvo y i, muuten arvo (y i - 1). Primitiivien toteutus / 15 Ympyrä (jatkuu) Parametrin p arvo seuraavassa pisteessä (x i +1, y i+1 ) voidaan laskea iteratiivisesti edellisessä pisteessä lasketusta arvosta. Edellä laskettua lauseketta muokkaamalla saadaan: p i+1 = 2[(x i +1) + 1] 2 + y i+1 2 + (y i+1-1) 2 2r 2 = p i + 4x i + 6 + 2(y i +12 - y i2 ) 2(y i+1 - y i ) = p i + 4x i + 6, jos y i+1 = y i = p i + 4x i + 6 4y i + 4, jos y i+1 = y i + 1 aloituspisteessä (0, r) lauseke on: p 1 = 3 2r. Primitiivien toteutus / 16 8
Ympyräkoodi procedure bres_circle (x_center, y_center, radius : integer); var p, x, y : integer; procedure plot_circle_points; { 8 symmetric positions } begin set_pixel(x_center + x, y_center + y); set_pixel(x_center - x, y_center + y); set_pixel(x_center + x, y_center - y); set_pixel(x_center - x, y_center - y); set_pixel(x_center + y, y_center + x); set_pixel(x_center - y, y_center + x); set_pixel(x_center + y, y_center - x); set_pixel(x_center - y, y_center - x) end; Primitiivien toteutus / 17 Ympyräkoodi (jatkuu) begin { bres_circle } x := 0; y := radius; p := 3-2 * radius; while x < y do begin plot_circle_points; if p < 0 then p := p + 4 * x + 6 else begin p := p + 4 * (x - y) + 10; y := y - 1; end; x := x + 1; end if x = y then plot_circle_points; end; Primitiivien toteutus / 18 9
Aliasoituminen l. vieraantumisongelma rasteriviivojen piirtämisessä piirtosuunta vaikuttaa viivan tiheyteen tiheys = 8 / (8 * 2) tiheys = 8 / 8 viivan leveys voi olla vain kokonaisluku xy-suunnissa vinon viivan reuna on porrasmainen (jagged) HUOM! Sama ongelma koskee myös muita graafisia kuvioita. Primitiivien toteutus / 19 Antialiasointi-diat Primitiivien toteutus / 20 10
Antialiasointi Antialiasointi = vieraantumisongelman ehkäiseminen tavallisin menetelmä on kunkin pikselin sävyttäminen sen mukaan, kuinka suurelta osin piirrettävä kuvio peittää sitä: Primitiivien toteutus / 21 Antialiasointi (jatkuu) viivan reunat sumenevat, mutta keskimäärin tasoittuvat mahdollista vain sävynäytöllä (ei mustavalkealla) erityislaitteilla mahdollista pikselien hienosäätö (pixel phasing): porraskohdissa olevia pikseleitä siirretään lähemmäs oikeaa viivaa pikselien suuruutta säädetään viivan kaltevuuden mukaan Primitiivien toteutus / 22 11
ALUEEN TÄYTTÄMINEN Kaksi perusperiaatetta: 1) konversio janoiksi pyyhkäisyjuoville 2) värin levittäminen rasterissa Primitiivien toteutus / 23 Juovakonversio etsitään kultakin pyyhkäisyjuovalta ne janat, jotka jäävät monikulmioalueen sisälle y1 y2 y3 y4 y5 y6 y7 y8 x = 1 2 3 4 5 6 87 9 10 Primitiivien toteutus / 24 12
Juovakonversio (jatkuu) voidaan laskea joka juovalle (y=vakio) toisistaan riippumatta, missä kohdissa (x-arvo) juova leikkaa monikulmion reunan tehokkaampaa, jos hyödynnetään koherenssia (so. vierekkäisten juovien samankaltaisuutta): saman reunajanan perättäiset leikkauspisteet juovien kanssa voidaan laskea inkrementaalisesti, kuten janan rasteroinnissa (DDA, Bresenham) kutakin juovaa leikkaa vain osa janoista; likimain sama aktiivinen janajoukko leikkaa myös seuraavaa juovaa (samassa x- järjestyksessä) singulariteettiongelma: montako leikkausta lasketaan, jos pyyhkäisyjuova kulkee monikulmion nurkan kautta? ratkaistaan tarkastelemalla janojen suuntia... Primitiivien toteutus / 25 Päällekkäiset monikulmiot: prioriteettijärjestys B B scan line A C A C päällekkäisyysjärjestys eri osissa pyyhkäisyjuovaa: A B A C B A C B C Yleinen koherenssiperiaate: geometristen objektien järjestäminen Y-suunnassa (pyyhkäisyjuovittain) X-suunnassa (kunkin juovan sisällä) P-suunnassa (prioriteetti eli "syvyys") saa aikaan sen, että joudutaan kulloinkin vertailemaan vain pienellä aktiivisella alueella olevia objekteja keskenään. Näitä piilopinta-algoritmeja käsitellään kirjan luvussa 13 ja myöhemmällä luennolla. Primitiivien toteutus / 26 13
Rekursiivinen 4-naapurusto ja 8-naapurusto Reunojen määrittely? Mustetippa-algoritmi void boundaryfill(int x, int y, int fill, int boundary) { int current; } current = getpixel(x, y); if ((current!= boundary) && (current!= fill)) { setcolor(fill); setpixel(x, y); boundaryfill(x+1, y, fill, boundary); boundaryfill(x-1, y, fill, boundary); boundaryfill(x, y+1, fill, boundary); boundaryfill(x, y-1, fill, boundary); } Primitiivien toteutus / 27 Pisteen sijainti alueen suhteen Usein tarvitaan tietoa siitä, kuuluuko annettu piste mallin määrittämään alueeseen. Eri esitystavoille voidaan käyttää erilaisia menetelmiä: suoraan sijoittamalla koordinaatit epäyhtälöön esim. piste (2, 3) on 10-säteisen ympyrän sisällä: 2 2 2 + 3 < 10 vertaamalla avaruusjakoon ja luetteloon alueen soluista tutkimalla ovatko pisteen parametrit rajatuilla väleillä edellyttää parametrikuvauksen käänteismuunnosta " 1 f : ( x, y, z)! ( u, v) 2 tutkimalla geometrisesti, onko piste reunan sisä- vai ulkopuolella Primitiivien toteutus / 28 14
Point-in-polygon Odd-even rule, scan-line Asetetaan ääretön puolisuora tutkittavasta pisteestä lähtien ja lasketaan sen leikkauspisteet alueen reunaviivan (tai pinnan) kanssa. Koska äärettämän kaukana oleva puolisuoran pää on alueen ulkopuolella ja reunan ylitys merkitsee aina siirtymistä joko ulkoa sisään tai sisältä ulos, niin pariton määrä leikkauksia merkitsee pisteen olevan alueen sisällä n=1 n=3 n=4 Primitiivien toteutus / 29 Point-in-polygon Normaalivektorit ristituloilla, vektorien suunnat Ongelmatapaukset itseään leikkaavat tai sulkeutumattomat reunat Primitiivien toteutus / 30 15