Lys og materialer
Det vi gjør er å tildele objektet en farge som skal emiteres fra skjermen. Vi vet at i programmering f.eks. under MS-Windows gjør vi dette ved å velge en penn eller en brush eller en bitmat for den tegningen vi skal lage. Objektet er på en måte "selvlysende" og helt uavhengig av lyskilder, refleksjoner eller andre påvirkninger. Metaforen med penn og kost assosierer direkte til måten vi tegner på papir for hånd.
Dette er en modell som passer svært dårlig med naturen og som er ubrukbar, eller i hvert fall svært vanskelig å bruke, når vi skal modellere "virkeligheten" i tre dimensjoner. Dersom vi har slike ambisjoner må vi ta hensyn til belysning, til geometri og til objektets materialegenskaper. Vi må kunne modellere objektets egenskaper når det gjelder å absorbere og reflektere lys, og vi må kunne handtere ulike typer lys og flere lyskilder. |
Når vi sier at vi skal modellere "virkeligheten" så er dette en sannhet med sterke modifikasjoner. Det er ikke fruktbart å oppfatte mekanismene for lys og materialegenskaper i grafisk databehandling som naturtro i den forstand at de modellerer termiske og optiske egenskaper ved lys og materialer etter fysikkens lover. Det som tilbys er imidlertid en mental og matematisk modell som er rimelig intuitiv og er et godt verktøy for å oppnå ønskede effekter.
Modellen er bygget på et samspill mellom lysegenskaper og materialegenskaper, samt geometriske beregninger basert på overflatenormaler, lysretninger og øyets plassering. De sentrale OpenGL rutinene er:
glLight() //for å bestemme lyskilder glMaterial() //for å bestemme materialegenskaper
Lystyper
...og tilhørende "materialrespons"
La oss først se på en inndeling i aktuelle lystyper. De engelske betegnelsene er nevnt for å referere til litteraturen.
I alle kallene på glLight nedenfor inngår parametern lno. dette angir hvilken lyskilde vi bestemmer egeneskapene til. OpenGL skal ha minst 8 lyskilder, GL_LIGHT0,GL_LIGHT1 osv.
I alle kallene til glMaterial nednefor inngår parametern side. side kan ha ha verdiene GL_FRONT, GL_BACK eller GL_FRONT_AND_BACK. Effekten av dette henger sammen med hva slags belysningsmodell vi har valgt, se oveskriften Belysningsmodell.
Bakgrunnslys
(Ambient light)
Dersom vi skal kople dette opp mot virkeligheten, må vi betrakte det som et allestedsnærværende lys uten retning. Vi må tenke oss at det er resultatet av refleksjoner av refleksjoner, og utgjør en allminnelig bakgrunnsbelysning. Bakgrunnslys gir en flat lyssetting. Siden lyset ikke er retningsbestemt, vil det ikke gi mening å beregne refleksjoner i forhold til flaters vinkel med synsretning eller lysretning (som ikke er definert).
Et punkt eller en flate vil gi fra seg et lys som er et resultat av det spesifiserte bakgrunnslyset og de spesifiserte materialegenskapene. Dersom vi har hvitt (1,1,1) bakgrunnslys og materialegenskaper som reflekterer bare rødt (1,0,0) vil objektet se ensfarget rødt og flatt ut. Vi setter opp bakgrunnslys og materialets respons på bakgrunnslys slik:
glLight(lno,GL_AMBIENT,Lar,Lag,Lab,alfa) glMaterial(side,GL_AMBIENT,Mar,Mag,Mab,alfa)
(alfa-verdien bestemmer gjennomskinnlighet/gjennomtrenglighet og side er GL_FRONT, GL_BACK eller GL_FRONT_AND_BACK, se overskriften Belysningsmodell)
Matematisk kan den utstrålte intensiteten (Rød-, Grønn- og Blå komponent) fra bakgrunnslys i et punkt uttrykkes slik:
Iar=Lar·Mar Iag=Lag·Mag Iab=Lab·Mab
Selv om materialet er bestemt, kan vi oppnå effekter ved å endre bakgrunnslyset. Hvis vi f.eks. har rent grønt lys (0,1,0) og et objekt bare reflekterer rødt (1,0,0) vil objektet ikke reflektere noe som helst og det vil opptre som svart (0,0,0). Vi ser at en nedtrapping av intensiteten i bakgrunnslyset, f.eks. (0.5,0.5,0.5) vil svekke utstrålingen fra objektet. Materialparametrene angir andel reflektert lys av de forskjellige innkommende komponentene
Retningsbestemt lys
(Diffuse light)
Dette lyset har en retning. Det kan komme fra en punktkilde, eller det kan komme uendelig langt borte fra og derfor opptre som parallelle "lysstråler". Det betyr at dette lyset kan bidra til en lyssetting som framhever dybden i bildet. Vi har en lysretning og tar hensyn til denne retningen og flatens orientering når vi skal bestemme intensiteten som reflekteres fra en flate.
Retningsbestemt lys reflekteres likt i alle retninger. Figuren nedenfor viser at den lysenergien som treffer en flate er omvendt proposjonal med vinkelen mellom normalen på flaten og lysretningen. Det vil si at energien pr flateenhet minsker med vinkelen. Det er bare interessant med vinkler melom 0 og 90 grader.
Vi må også vurdere vinkelen mellom flaten og øyet. Lys reflektert fra en flateenhet er proposjonal med cosinus mellom synsvinkelen og flatenormalen. Det betyr at dersom vi ser rett på en flate, møter øyet maks energi pr flateenhet. Dersom vi ser skrått på en flate representerer det vi ser en større flate, men vi mottar mindre energi pr enhet. Disse to forholdene oppveier hverandre, og vi (OpenGL) trenger ikke ta hensyn til synsvinkelen ved beregning av retningsbestemt lys.
glLight(lno,GL_DIFFUSE,Ldr,Ldg,Ldb,alfa) glMaterial(side,GL_DIFFUSE,Mdr,Mdg,Mdb,alfa)
(alfa-verdien bestemmer gjennomskinnlighet/gjennomtrenglighet og side er GL_FRONT, GL_BACK eller GL_FRONT_AND_BACK, se overskriften Belysningsmodell)
Matematisk kan den utstrålte intensiteten (Rød-, Grønn- og Blå komponent) fra det retningsbestemte lyset i et punkt uttrykkes slik:
Idr=Ldr·Mdr·cos(v) Idg=Ldg·Mdg·cos(v) Idb=Ldb·Mdb·cos(v)
Der v er vinkelen mellom flatenormalen og retningen mot lyset. v må være mellom 0 og 90 grader.
cos(v) kan vi som kjent uttrykke ved hjelp av skalarproduktet mellom normalevektoren på flaten, N, og lysretningen L. Dersom vi antar at de to er normaliserte, dvs. at de har lengde 1, kan vi skrive:
Idr=Ldr·Mdr·(N·L) Idg=Ldg·Mdg·(N·L) Idb=Ldb·Mdb·(N·L)
Se modulen Algebra
Skinnende lys
(Specular light)
Denne lyskomponenten er et forsøk på å modellere den skarpe refleksen vi finner i "skinnende" materialer. F.eks. hvitt lys som reflekterer fra en rød plastikkball gir inntrykk av en hvit flekk på ballen. Vi snakker nå om refleksjon som ikke spres likt i alle retninger.
Modellen tilsier at intensiteten i reflektert lys er avhengig av b og den vil være størst for b=0, altså når synsvinkelen er lik vinkelen mellom flatenormalen og lysretningen. Det finnes ulike måter å beregne lysintensiteten avhengig av b. I OpenGl er beregningen av den skinnende intensiteten avhegngig av hva slags lysmodell vi har spesifisert. Mer om dette under Overskriften Lysmodell.
Det inngår to spesifikasjoner for skinnende (specular) lys, komponenten av skinnende lys og refleksonsintensiteten (shininess):
glLight(lno,GL_SPECULAR,Lsr,Lsg,Lsb,alfa) glMaterial(side,GL_SPECULAR,Msr,Msg,Msb,alfa) glMaterialf(side,GL_SHININESS,sh);
(alfa-verdien bestemmer gjennomskinnlighet/gjennomtrenglighet og side er GL_FRONT, GL_BACK eller GL_FRONT_AND_BACK, se overskriften Belysningsmodell)
Vi kan ta Phongs belysningsmodell som utgangspunkt for resonnementet. Phongs smodell tar utgangspunkt i at refleksjonen faller ved økende b som cos opphøyd i en eksponent: cosn(b). Siden cos <1, vil økende n gi skarpere refleksjonskarakteristikk. Vi kan etter Phongs modell skrive denne lyskomponenten slik den reflekteres fra et materiale som:
Isr=Lsr·Msr·cossh(b) Isg=Lsg·Msg·cossh(b) Isb=Lsb·Msb·cossh(b)
Igjen, dersom vi har normaliserte R og V vektorer, kan vi skrive:
Isr=Lsr·Msr·(R·V)sh Isg=Lsg·Msg·(R·V)sh Isb=Lsb·Msb·(R·V)sh
Utstrålt lys
(Emitted light)
Dette er lys som sendes ut fra et materiale, og er den lystypen som korresponderer med enkle fargeangivelser som ikke tar hensyn til lys eller geometri, slik vi nevnte innledningsvis. Vi lager selvlysende objekter. Dersom vi arbeider med objekter i 3D og bruker slikt lys, er flatenes vinkel med øyet uinteressant og fargeleggingen blir helt flat. Det er eventuelt bare formen på objektene som kan illudere 3D.
Denne lyskomponenten skiller seg fra de andre , siden den ikke kombinerer lys og materialegenskaper. Dette "lyset" er en materialegenskap.
glMaterial(side,GL_EMISSION,Mer,Meg,Meb,alfa)
(alfa-verdien bestemmer gjennomskinnlighet/gjennomtrenglighet og side er GL_FRONT, GL_BACK eller GL_FRONT_AND_BACK, se overskriften Belysningsmodell)
Matematikken i dette koker ned til:
Ier=Mer Ieg=Meg Ieb=Meb
Lysegenskaper
Av og på
En OpenGL-implementasjon skal ha minst 8 lyskilder. Det er opp til oss å velge hvilke vi vil bruke. De vi bruker må vi gi egenskaper og vi må skru dem på. Vi skrur på og av en lyskilde ved:
glEnable(GL_LIGTHn) glDisable(GL_LIGHTn)
der n er et tall fra 0 og opp til det antall lyskilder vi rår over. Vi kan også skru på og av lys i sin alminnelighet:
glEnable(GL_LIGTHING) glDisable(GL_LIGTHING)
Posisjon
Et lys kan ha en bestemt posisjon i rommet eller det kan ha en retning, som om det kommer uendelig langt borte fra. Posisjonen settes ved:
glLight(lno,GL_POSITION,x,y,z,w)
Der w tolkes slik: w=0 angir at lyset har en retning, og (x,y,z) angir retningen. w=1 angir at lyskilden er plassert i (x,y,z)
Default verdier er (0,0,-1,0), altså en lysretning som sammenfaller med den negative z-aksen.
Svekking av lysstyrke
For retningsbestemt og skinnende lys har vi mulighet for å angi hvordan lysintensiteten endres avhengig av hvor lang strekning lyset har vandret. Dette kan være en teknikk for å la fjerne ting bli svakere belyst, enten for å understreke avstand eller for modellere "virkeligheten" slik at vi tenker oss at lys av ulike årsaker svekkes med avstand (tåke, røyk, forurensing).
Det vanlige er å korrigere lysintensiteten med en faktor, attenuation factor, etter følgende mønster:
,
der dL er avstanden til lyskilden og c-konstantene er vekter. Vi ser at ikke alle konstantene kan være null. Dersom c1=1 og c2=c3=0 så er lysintensiteten usvekket og konstant. Dette er default verdiene.
glLightfv(lno,GL_CONSTANT_ATTENUATION,c) glLightfv(lno,GL_LINEAR_ATTENUATION,cl) glLightfv(lno,GL_QUADRATIC_ATTENUATION,c3)
Spotlights
Lys kan gis en avgrensning i form av en kjegle, spotlight-effekt, for å lyse opp bare deler av en scene. Vi kan bestemme kjeglen og vi kan bestemme hvordan intensiteten innen kjeglen endres.
Vinkelen mellom aksen i kjeglen og sidekanten kan i OpenGL bestemmes ved
glLight(lno,GL_SPOT_CUTOFF, v),
der lno indekserer en lyskilde og v en vinkel mellom 0 og 90. Default er retningen nedover den negative z-aksen og spredningen har spesialverdien 180. Det vil si at lyset spres i alle retninger.
Variasjon i intensiteten innen kjeglen kan bestemmes ved
glLight(lno,GL_SPOT_EXPONENT,e)
hvor e angir en potens for cos til vinkelen mellom center i kjeglen og lysretningen. Sml Phongs modell for skinnende lys, ovenfor.
Noen fallgruber forbundet med bruk av spotlights er berørt i modulen: Skygge og glatting
Belysningsmodell
Vi kan sette noen egenskaper i OpenGL som påvirker hvordan lys/materiale beregninger foregår. Den aktuelle rutina er:
glLightModelfv(...)
Det er tre settinger som er aktuelle, generelt bakgrunnslys, sidespesifikasjon og refleksjonsberegninger.
Generelt bakgrunnslys
Vi kan sette et generelt bakgrunnslys som ikke har noe med en spesiell lyskilde å gjøre. Dette er et allestedsnærværende ambient lys.
glLightModelfv(GL_LIGHT_MODEL_AMBIENT,Lgr,Lgg,Lgb,alfa)
Default verdier er (0.2,0.2,0.2,1.0), som gir et svakt hvitt lys. Bidraget til intensiteten i et punkt basert på dette er avhengig av materialets respons på bakgrunnslys (ambient light):
Ier=Lgr·Mar Ieg=Lgg·Mag Ieb=Lgb·Mab
Tosidige materialer
Dersom vi ønsker å ha separat lysberegning på begge sider av en flate kan vi kalle følgende:
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,1);
Refleksjonseffekt
Vi kan påvirke måten refleksjonseffekten beregnes. Vi har ovenfor sett på Phongs modell for refleksjon, Shininess.
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER);
Default beregnes skinnende lys som om øyepunktet er uendelig lang borte, og vinkelen som beskriver synsretningen er lik for alle beregninger. Hvis vi skrur på "local viewer", som vist ovenfor, vil OpenGl beregne synsvinkelen for hver vertex. Dette vi da generelt gi et "bedre" resultat, men det koster. Merk at det er bare beregning av skinnende lys ,shininess, som krever en beregning som tar hensyn til øyets plassering.
Oppsummert
OpenGL arbeider med alle de fire lyskomponentene vi har nevnt ovenfor: Emittert lys (Le), bakgrunnslys(La), retningsbestemt lys (Ld) og skinnende lys (Ls). I tillegg har vi altså det generelle bakgrunnslyset som ikke refererer til noen spesiell lyskilde (Lg). Totalt får vi, med referanse til resonnementene ovenfor:
summert opp over alle n lyskildene. Dette tilsvarer den beregningen som skal skje når OpenGL bestemmer verdien i et pixel. Vi må dessuten huske at vi får et tilsvarende uttrykk for hver fargekomponent. Vinkelberegningene, skalarproduktene, settes til 0 dersom vinkelen mellom de to berørte vektorene er større enn 180 grader. Spotlighteffekter inngår ikke i uttrykket ovenfor.
Vektorene N, L, R og V blir indirekte bestemt ved at vi plasserer objekter, øyepunkt og lyskilder i rommet ved hjelp av de vanlige transformasjonene.
En lyskilde
Et eksempel på spesifikasjon av en lyskilde:
Glfloat light_ambient[]={0.0,0.0,0.0,1.0}; Glfloat light_diffuse[]={1.0,1.0,1.0,1.0}; Glfloat light_specular[]={1.0,1.0,1.0,1.0}; Glfloat light_position[]={1.0,1.0,1.0,0.0}; glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient); glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse); glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular); glLightfv(GL_LIGHT0,GL_POSITION,light_position); glEnable(GL_LIGHING); glEnable(GL_LIGHT0);
Merk spesielt den siste verdien i posisjonsvektoren. Dersom denne er 0 betyr det at vi spesifiserer en lysretning. Dersom den er 1 så betyr det at vi spesifiserer et punktlys.
Et materiale
Et eksempel på spesifikasjon av et materiale (messing):
Glfloat mat_ambient[]={0.329412f,0.223529f,0.027451f}; Glfloat mat_diffuse[]={0.780392f,0.568627f,0.113725f }; Glfloat mat_specular[]={0.992157f, 0.941176f, 0.807843f }; glMaterialfv (GL_FRONT,GL_AMBIENT,mat_ambient); glMaterialfv (GL_FRONT,GL_DIFFUSE,mat_diffuse); glMaterialfv (GL_FRONT,GL_SPECULAR,mat_specularv); glMaterialf (GL_FRONT,GL_SHININESS,27.89743616);
Programmet Materialeditor som er beskrevet i modulen Materialeditor er nyttig for å finne fornuftige parametere når vi skal sette opp et materiale.
Normaler
I lysmodellen inngår beregning av vinkelen mellom retninger ved hjelp av dotproduktet av de tilhørende vektorene. Disse beregningene forutsetter at vektorene er normaliserte, dvs at de har lengde lik 1. Vi kan angi normaler med vilkårlig størrelse ved hjelp av glNormal- rutina. Dersom vi skal få OpenGL til å håndtere disse som normaliserte i lysberegniger så må vi skru på normalisering:
glEnable(GL_NORMALIZE)
Denne egenskapen er default slått av.
Det er også viktig å passe på at normalene angis slik at de systematisk peker samme retning, i den forstand at de går på samme side av en flate. Husk at normalene beregnes ved et kryssprodukt etter høyrehåndsregelen. Se modulen Algebra OpenGL har en funksjon som hjelper oss å bestemme retninger på flater:
glFrontFace(GL_CW) glFrontFace(GL_CCW)
Med GL_CW angir at vi vil spesifisere punkter i et polygon med klokka (ClockWise) og GL_CCW mot klokka (CounterClockWise). Dette er sett mot fronten, forsiden på flaten. GL_CCW er default.
Lyssetting
Vi kan ganske greitt slå fast at frihetsgradene er mange og det er ikke annet enn prøving, feiling og erfaring som kan føre fram til god lyssetting. Kombinasjonen av lys og materialer er så mange at det i starten virker ganske forvirrende og vanskelig å realisere en ide.
Foruten at vi har alle de komponentene å spille på som vi har drøftet ovenfor, har vi minst 8 lyskilder som kan gis forskjellige egenskaper for de forskjellige lystypene. Vi kan flytte lysene og vi kan endre egenskapene dynamisk.
Hvis vi i tillegg blander inn teksturer og gjennomskinnelighet blir det ganske komplisert.