Fra modell til skjerm
Et viktig poeng er å forstå anvendelsen av de to matrisene som brukes i OpenGL. Vi velger hvilken matrise vi vil bearbeide ved kommandoene:
glMatrixMode(GL_PROJECTION); glMatrixMode(GL_MODELVIEW);
Vi vet fra kodeeksemplene i dette materialet at vi skriver typisk når vi skal sette opp en projeksjon:
//projeksjonsmatrisa: glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(...); // set back matrix mode: modell og view glMatrixMode(GL_MODELVIEW);
GL_PROJECTION er den matrisa som benyttes i steg 4 ovenfor, mens GL_MODELVIEW er den matrisa som benyttes i stegene 1 og 2.
Et typisk forarbeid til uttegning, som altså bearbeider matrisa GL_MODELVIEW er som vi har sett mange eksempler på:
glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // position the eye relative to scene gluLookAt(...); // Set modell in position glTranslatef(), glRotatef(...); ...
der vi ser at både øyeplassering, gluLookAt, og modelltransformasjoner, glTranslatef og glRotatef, inngår i spesifikasjon av G_MODELVIEW-matrisa.
Steg 5 i oversikten ovenfor, fram til den aktuelle skjermen, setter vi OpenGL typisk slik:
glViewport(...);
som jo angir hvor i vinduet vi vil se det projiserte bildet.
klippebetingelsene, steg 3, er gitt når vi spesifiserer den synspyramiden vi vil anvende. F.eks. ved:
gluPerspective(vy,aspect,front,back)
eller
glOrtho(xmin,xmax,ymin,ymax,front,back)
Transformasjoner i rommet
- steg 1
Dette har vi diskutert i et eget kapittel og vi har vist at alle transformasjoner for å plassere objekter i rommet er komplett beskrevet i matriseform, og at sammensatte operasjoner lar seg slå sammen til en matrise. Se modulen 2D transf. og modulen 3D transf.
Omregning til View-koordinatsystemet
- steg 2
Vi ønsker å spesifisere et eget koordinatsystem som er gitt av de spesifikasjonene som angir måten vi betrakter den aktuelle scenen på. Ulike grafiske tekster beskriver dette på litt forskjellig måte og med litt forskjellig begrepsapparat. Jeg bruker de begrepene vi finner hos Foley. I hovedtrekk er hensikten å transformere modellen slik at den er beskrevet i et eget koordinatsystem med tre dimensjoner, Viewing systemet, beskrevet med aksene (u,v,n). Dette systemet er bestemt utfra følgende spesifikasjoner av hvordan vi vil se modellen:
- View Reference Point, VRP, som er et punkt i et plan parallellt med projeksjonsplanet. Vi kan godt tenke på dette planet som projeksjonsplanet.
- View Reference Normal, VRN, som er en normal til projeksjonsplanet, oppreist i VRP. VRN faller sammen med n-aksen i det nye koordinatsystemet
- Et øyepunkt som ligger på VRN. Dersom vi har en parallellprojeksjon betrakter vi øyepunktet uendelig lang ut på VRN
- En angivelse av hva som er opp, VUP. Dette for å skille de to andre aksene, u og v. De gis retninger slik at u,v,n er et høyrehåndssystem.
VRN-vektoren er altså vektoren fra VRF til øyet.
I OpenGL kan vi spesifisere betraktningsmåten på mange forskjellige måter. Den funksjonen som ligger tettest opp til beskrivelsen ovenfor er:
gluLookAt( ex,ey,ez, // øyets plassering vx,vy,vz // det punktet vi ser mot (VRP) ux,uy,uz) // opp vektor (VUP)
Transformasjon av modellen fra det opprinnelige x,y,z-koordinatsystemet til det nye systemet (u,v,n) kan gjøres ved en matrise som er sammensatt av flere deloperasjoner, translasjoner og rotasjoner. Problemet med å beskrive en slik transformasjon er i prinsipp ganske likt det generelle problemet å rotere om en vilkårlig akse. Det er ganske komplisert å tegne og beskrive en slik manøver generellt, se f.eks. Foley 5.7.
Fordelen når vi først har funnet en slik matrise er at den kan multipliseres med den generelle modelltransformasjonsmatrisen til en matrise som kombinerer modelltransformajoner med overgangen til View-koordinatsystemet. Vi knytter altså sammen steg1 og steg 2 på oversiktsfiguren i en enkelt matriseoperasjon.
OpenGL vedlikeholder en matrise, karakterisert med GL_MODELVIEW som kombinerer steg 1 og steg 2 på oversiktsfiguren i starten av kapitlet.
Klipping
- steg 3
Vi så ovenfor at spesifikasjonenen av både perspektivprojeksjon og parallellprojeksjon avgrenset den delen av rommet rommet vi ønsket å få med i projeksjonen. Det betyr at vi må klippe mot denne begrensningen. Vi har valgt å ikke gå inn på klippeproblematikk i dette materialet. Derfor forfølger vi ikke dette her, men det er verdt å merke seg at den matriseformuleringen vi argumenterte for ovenfor også inngår i en transformajon til et spesielt klippekoordinatsystem som forenkler klippingen.
Projeksjon ned i et plan
- steg 4
Etter at vi har fått modellen over i vårt nye koordinatsystem, View-systemet, kan vi gå løs på selve projeksjonen fra rommet inn i et plan. For å holde oss ajour med terminologien i Foleys bok og OpenGL angir vi aksene i det videre resonnementet med (x,y,z), i stedet for (u,v,n) som vi bruke i resonnementet ovenfor.
OpenGL vedlikeholder en matrise, karakterisert med GL_PROJECTION som realiserer steg 4.
Vi må bestemme oss for hvordan vi skal betrakte verden fra det øyepunktet vi har definert. To måter å gjøre dette på i OpenGL er:
Perspektivprojeksjon
gluPerspective(vy,aspect,front,back)
vy angir vinkelen fra synsretningen i y-retningen,
aspect angir forholdet mellom x-vinkelen og vy.
Disse spesifikasjonenen angir en pyramide. front og back skjærer av pyramiden med to plan parallelle med projeksjonsplanet.
Paralellprojeksjon
glOrtho(xmin,xmax,ymin,ymax,front,back)
Beskriver rett og slett en boks som avgrenser det som skal være med i projeksjonen.
Matematikken
Vi konsentrere oss om perspektivprojeksjonen. Vi må altså projisere vår figur ned i et plan ved hjelp av projeksjonslinjer som samles i øyepunktet. Vi kan betrakte problemet slik.
Resonnementetene blir helt like for x- og y-aksen.
Vi ser uten videre at vi kan sette opp:
Vi tar utgangspunkt i matrisen nedenfor:
Det er denne matrisen som benyttes av OpenGL når vi arbeider med matrisemodus GL_PROJECTION. I hvert fall er det en fruktbar mental modell som hjelper oss å skille mellom GL_MODELVIEW som vi diskuteret ovenfor og GL_PROJECTION. Vi ser at Mper er uavhengig av z. Det er en fordel siden vi skal ha en fast matrise å forholde oss til.
Hvis vi anvender matrisen på P får vi:
X=x, Y=y, Z=z, W=z/d
som ikke er akkurat hva vi ønsket oss. Vi har imidlertid oppnådd en generell matriseoperasjon og vi kan rydde opp til slutt:
Vi dividerer med W og får som ønsket:
Den siste divisjonen er det som kalles homogenisering av en resultatvektor. OpenGL foretar alltid en slik homogenisering, se modulen: Homogenisering
Framstilling på skjermen
- steg 5
Alle våre resonnementer så langt har foregått med et abstrakt todimensjonalt koordinatsystem for øyet. Vi har ikke tatt stilling til verken begrensninger eller presisjon. Når vårt bilde eller modell skal framstilles på skjermen vet i at vi må forholde oss til et endelig antall punkter i x- og y-retningen. Vi må finne en generell måte å komme fra et generelt koordinatsysem som er hensiktsmessig for modellbeskrivelsen og over til framstillingssystemet, skjermen.
Det ville være for snevert dersom vi måtte beskrive alle våre modeller i skjermkoordinater. Det vil ha flere ulemper:
- Vi ville kunstig måtte transformere modellen vår, enten den opprinnelig var beskrevet i mm eller km, til punkter.
- Vi måtte lage kode som hele tiden undersøkte og tok hensyn til den aktuelle skjermen.
- Vi ville være tvunget til å operere i heltallskoordinater, og vi ville utsette oss for akkumulerte avrundingsfeil.
Vi ønsker å kombinere maksimal frihet i modellbeskrivelsen med en fleksibel mapping til de til enhver tid aktuelle skjermkoordinatene. I tradisjonell grafisk databehandling er det en tradisjon for å kalle modellkoordinatene World Coordinate System (wc) eller Logical Coordinate System (lc) og skjermkoordinatsystemet for Device Coordinate System (dc).
Når vi skal framstille noe på skjermen ønsker vi å avgrense både den delen av verden vi vil betrakte og den delen av skjermen som skal benyttes.
I grafisk databehandling er navnebruken litt forvirrende siden den delen av verden vi betrakter blir kalt Window, mens den delen av skjermen vi benytter kalles Viewport. årsaken til dette er at terminologien grafisk databehandling er eldre enn de vindussystemene vi kjenner i dag.
Vi spesifiserer altså Window i wc og Viewport i dc.
La oss anta at Window er spesifisert ved Wxl,Wxr,Wyt,Wyb og Viewport ved Vxl,Vxr,Vyb,Vyt.
En mapping av punktet P(xw,yw) i wc til P(xv,yv) i dc er da beskrevet i hht følgende:
og
som gir oss
og
Vi kan skrive ut disse slik at vi ser at de inneholder både en skalerings- og en translasjonsfaktor:
xv=sx(xw-Wxl)+Vxl og yv=sy(yw-Wyb)+Vyb
Hvis vi betrakter (xw-Wxl) og (yw-Wyb) som koordinater relativt til origo i Window, ser vi at dette også lar seg formulere som en matrise.
Dersom utsnittet vi betrakter innebærer at deler av figuren faller utenfor innbærer avbildningen også klipping. Vi forfølger ikke dette her. Den mest vanlige klippealgoritmen for linjer er Cohen-Sutherlands klippealgoritme, se Foley kapittel ????.
Vi ser lett at mappingen fra wc til dc kan være en måte både å zoome og fordreie på. Vi kan også flytte Window for å oppnå panorering.
MS Windows
Alle grafiske vindussystemer med respekt for seg selv har rutiner for å mappe verden til skjermen, slik også med MS Windows. MS Windows opererer med begrepene Logical Coordinates for modell-koordinater og Device Coordinates for skjerm-koordinater (og printer/plotter koordinater). MS Windows forutsetter dessuten at også de logiske koordinatene er beskrevet som heltall. Vi refererer hele tiden til det aktuelle vinduet, ikke hele skjermen. Funksjonsbatteriet er slik:
SetWindowOrg | Setter origo i vinduet, logiske koordinater |
SetWindowExt | Setter utstrekningen i vinduet, logiske koordinater |
SetViewportOrg | Setter origo i viewport, device koordinater |
SetViewportExt | Setter utstrekningen i viewport, device koordinater |
LPtoD | Tar et enkeltpunkt fra modell til skjerm |
DPtoLP | Tar et enkeltpunkt fra skjerm til modell. Nyttig ved tolkning av museinput inn i en modell |
SetMappingMode | Angir typen av mapping. |
I Visual C++ er rutinene knyttet til MFC-klassen som representerer Device Context, CDC. Den siste rutinen, SetMappingMode, er interessant. Den gir oss en masse muligheter for å sette føringer på mappingen. F.eks. kan vi angi at mappingen skal være lik langs begge aksene, slik at vi ikke får forvrengninger. Vi kan også spesifisere mappingen slik at f.eks. et rutenett beskrives med fast størrelse, f.eks. 1 cm på både printer og skjerm. (Dette forutsetter at driverne er gode og nøyaktige). Se beskrivelsen av Windows eller MFC for en nærmere detaljer.
OpenGL
I OpenGL frigjør vi oss fra en av de begrensningen som ligger i MS Windows og vi kan operere med flytetall i modellbeskrivelsen. OpenGL rutina som avgrenser det området på skjermen vi skal bruke heter glViewport(...). Modell avgrensningen er litt mer komplisert enn i MS Windows fordi vi generelt skal forholde oss til 3 dimensjoner.
I prinsippet kan denne siste transformasjonen fram til skjermkoordinater betraktes helt uavhengig av alle stegene ovenfor. Vi har i de forrige stegene arbeidet oss fram til en planprojeksjon som i sin tur skal mappes ut på skjermen. I OpenGL bestemmer vi det området på skjermen der vi ønsker å betrakte scenen vår ved
glViewport(xmin,xmax,ymin,ymax)
En produksjonskjede
De stegene vi har diskutert ovenfor legger forholdene til rette for effektivisering ved hjelp av spesialisert hardware. Vi kan betrakte jobben fra modell til skjerm som en produksjonskjede med rimelig avgrensede og standardiserte oppgaver. Det er lett å se for seg en produksjonslinje der koordinatene tres gjennom og bearbeides i forskjellige stadier.