INF3320 - Obligatorisk oppgave 2 Innleveringsfrist: 23. september (Revisjon 4. september 2003) I denne oppgaven skal vi se på transformasjoner og interaktivitet. Vi skal lage et lite program som implementerer en virtuell trackball. En virtuell trackball er en manipulator som efterligner en trackball, og gir brukeren mulighet for å rotere scenen på en intuitiv måte. Denne manipulatoren er veldig mye brukt i 3D-applikasjoner. For å få en ide om hvordan denne manipulatoren fungerer så kan man tenke seg en glasskule med scenen inne i. Senteret til kulen står fast. Man kan tak i kulen på et sted og dra kulen i en retning. Kulen vil da rotere om sitt senter med den retningen man drar. Vanligvis har man også mulighet for å flytte scenen i forhold til kulen (translasjon) og å zoome inn og ut. Vi skal dog bare se på rotasjon i denne oppgaven, men å implementere translasjon og zoom kan være en utfordring til interesserte sinn. Vi skal holde scenen veldig enkel i denne oppgaven, scenen skal bare bestå av en enkel kube tegnet med linjer. Oppgaven kan løses enten ved hjelp av enhetskvarternioner eller rotasjonsmatriser (med litt fiffighet kan man la OpenGL lage rotasjonsmatrisene for seg), men vi anbefaler å bruke enhetskvarternioner. 1 En virtuell trackballs detaljer Vi bruker den virtuelle trackballen for å definere en rotasjon utifra to skjermkoordinater. Vi skal i denne seksjonen beskrive hvordan man utleder denne rotasjonen. Først må vi gjøre om skjermkoordinatene til 3D-koordinater. Vi gjør dette ved å utføre en parallelprojeksjon av vinduet ned på en kule. For å presisere; vi antar at w er bredden og h er høyden på vinduet og at (x 1, y 1 ) og (x 2, y 2 ) er henholdsvis det første og det andre punktet på skjermen som skal definere rotasjonen. Første steg er å normalisere koordinatene slik at alle punktene i vinduet ligger i [ 1, 1] [ 1, 1] med senteret av vinduet i (0, 0). Vi lar (x, y ) være de normaliserte versjonene av (x, y) og får (x, y ) = (x/w 0.5, y/h + 0.5). (1) En liten detalj i dette uttrykket er at vi skifter fortegn på y-koordinatet. Grunnen til dette er simpelthen at y-aksen i skjermkoordinater peker nedover, mens y aksen i OpenGL peker oppover. Derefter projiserer vi de normaliserte koordinatene ned på kulen. Vi definerer r = x x + y y som er avstanden mellom punktet og senteret av skjermen (som 1
også er senteret av kulen). Hvis r < 1/2 så treffer punktet på kulen, og posisjonen til punktet på kulen er gitt ved (2x, 2y, 1 4r 2 ). (2) Derimot hvis r 1/2 så treffer ikke punktet på ballen, og vi velger å bruke det nærmeste punktet på kulen istedenfor. Dette punktet er gitt ved (x/r, y/r, 0). (3) Punktet ligger nå på kulen og vektoren fra senteret av kulen til punktet har lengde lik 1. Vi gjør denne projeksjonen for både (x 1, y 1 ) og (x 2, y 2 ). Vi har da to punkter på en enhetskule. For hvert av disse to punktene finner vi vektoren fra kulens senter og ut til punktet. Indreproduktet av de to vektorene gir cosinusverdien til antallet radianer som skal roteres, og ytreproduktet (kryssproduktet) gir retningen til aksen vi skal rotere om. 2 Enhetskvarternioner Kvarternioner er nok ukjent for de fleste, men enhetskvarternionene brukes veldig mye innen datagrafikk for å beskrive rotasjoner. Den store gevinsten med enhetskvarternioner vinner man når man skal sette sammen flere rotasjoner. Uttrykk for alle operasjonene man trenger å gjøre med enhetskvarternioner for å løse denne oppgaven er gitt i læreboken, men siden uttrykkene er ganske store, vil vi legge ut kildekode for noen av funksjonene på gruppesidene til kurset: q_multiply(float *q, float *a, float *b) Funksjonen multipliserer a med b og legger svaret i q. q_make(float *q, float *axis, float *rad) Funksjonen lager en kvarternion som beskriver en rad radianers rotasjon om aksen axis (som forøvrig må være normalisert) og legger svaret i q. Dette er helt analogt med parameterene man gir funksjonen glrotate. q_create_gl_matrix(float *m, float *q) Funksjonen lager en 4 4 transformasjonsmatrise fra kvarternionen q. Resultatet lagres i m. Når programmet starter må orienteringen p ha en verdi. Et fornuftig valg av en slik verdi er p = (1, [0, 0, 0]) som sier at man ikke skal rotere noe i det hele tatt. 2
3 Forslag til implementasjon Vi skal i denne seksjonen gi et forslag til hvordan man kan løse oppgaven. Indirekte blir det også her gitt en noen krav til hvordan programmet skal oppføre seg. Vi forutsetter her at glut brukes, men vi presiser at man står fritt til å velge programstruktur og vindusbibliotek for å løse oppgaven. Man må med andre ord ikke følge denne beskrivelsens valg av struktur. Vi trenger to orienteringer (rotasjoner) i dette programmet, og vi velger å beskrive disse ved hjelp av enhetskvarternioner. Vi trenger følgende tilstandsinformasjon i programmet: 1. Enhetskvarternionen p som beskriver den gjeldende orienteringen. 2. Enhetskvarternionen q som beskriver orienteringen da musknappen ble trykket ned. 3. En variabel som beskriver om venstre musknapper trykket ned eller ikke. 4. Koordinatene til muspekeren da musknappen ble trykket ned. 5. Størrelsen på vinduet. Vi vil nå beskrive de forskjellige funksjonene til programmet. 3.1 reshape_func For å registrere denne funksjonen i glut brukes glutreshapefunc. Denne funksjonen skal lagre størrelsen til vinduet og kalle funksjonen glviewport. 3.2 init_func Denne funksjonen kaller du selv fra main. Denne funksjonen skal sette OpenGLtilstand som ikke forandrer seg i løpet av programmet. Vi skal her definere en kameraprojeksjon. Bruk glmatrixmode for å skifte til GL_PROJECTION-matrisen. Bruk derefter glfrustum for å definere en passende projeksjon. Skift til slutt tilbake til GL_MODELVIEW-matrisen. 3.3 display_func For å registrere denne funksjonen i glut brukes glutdisplayfunc. Denne funksjonen skal gjøre følgende: 3
1. Slette skjermen. 2. Sette MODELVIEW-matrisen til enhetsmatrisen. 3. Translatere kameraet slik at man ser på origo. 4. Multiplisere med rotasjonen som p representerer. 5. Tegne kuben. Kjekke funksjoner for denne delen er gltranslate, glloadidentity, glmultmatrix, glutsolidcube og glutwirecube. 3.4 mouse_func For å registrere denne funksjonen i glut brukes glutmousefunc. Hvis musknappen blir trykket ned skal funksjonen gjøre tre ting: Først skal p kopieres til q (altså gjeldende orientering skal lagres), så skal man lagre x og y-koordinatene til muspekeren og til slutt skal man sette variabelen som beskriver om musknappen er nede. Hvis musknappen slippes opp skal man man bare oppdatere variabelen som beskriver om musknappen er nede eller ikke. 3.5 motion_func For å registrere denne funksjonen i glut brukes glutmotionfunc. Hvis ventre musknapp er trykket ned, så skal funksjonen gjøre følgende: 1. Definér r til å være lik orienteringen definert utifra koordinatet lagret i mouse_func og koordinatet som sendes med til motion_func (se seksjon 1). 2. Orienteringen p settes til å være kombinasjonen av q og r. Hvis man bruker enhetskvarternioner blir dette p = r q. 3. Man kaller glutpostredisplay slik at glut kaller display_func. 4 Innlevering Oppgaven er individuell og alle skal programmere hvert sitt program. Det er lov å samarbeide, men kopiering og avskrift av kildekode vil ansees som fusk. Ved mistanke om fusk kan man bli tatt ut til muntlig høring av oppgaven. 4
Innlevering av oppgaven består i at man sender en epost til gruppelærer med et tar-arkiv som heter brukernavn2.tar.gz hvor brukernavn er brukernavnet ditt. Arkivet skal inneholde fullstendig kildekode, men ikke kompilerte objekt (*.o)-filer (kjør make clean før du lager arkivet). For å lage et tar-arkiv gjør man følgende besvergelser: tar -cvf brukernavn2.tar brukernavn2/ gzip -9 brukernavn2.tar Første linje lager et arkiv av filene i mappen brukernavn/ og den andre linjen komprimerer arkivet. Programmet skal være programmert i enten C eller C++ og visualiseringen skal gjøres i OpenGL. Utover dette står man fritt til å velge om man vil bruke glut, Qt eller et annet vindusbibliotek. Dokumentasjon av oppgaven gjøres ved at kildekoden kommenteres og man legger ved en readme.txt fil i arkivet, slik at hovedidéer og hvorfor man har gjort som man har gjort kommer tydelig frem. Hvis det er noen spørsmål angående oppgaven kan disse rettes til gruppelærer enten på gruppeøvningene eller pr. epost. Lykke til! 5