Innhold:
- Innledning
- 3D-modeller, lys og shading
- Phong reflection model
- Noen tips i forhold til Blender og eksport (threejs)
Innledning
Vi har hittil sett hvordan vi legger farge og tekstur på 3D-modellene. Dette gir greie modeller, men fortsatt mangler det noe for å få skikkelig 3D-effekt.
Belysning er kanskje det viktigste momentet for å få 3D-modeller til å fremstå realistiske. Uten belysning vil 3D-modeller og scener i enkelte tilfeller fremstå som 2D. Et eksempel på dette er en kule konstruert av et antall trekanter. Uten lyseffekter vil denne fremstå som en 2D-sirkel. Med korrekt belysning vil den delen av kula som står mot lyset fremstå med klarere farge enn den som er vendt fra lyset.
I denne sammenheng ønsker vi, på best mulig måte, å simulere hvordan lys fra ulike kilder påvirker fargen til objektene/modellene. Nøyaktig simulering av hva som skjer i den virkelige verden er krevende siden lys kan komme fra mange kilder og retninger og ikke minst at lyset reflekteres og absorberes fra de fleste objekter/flater som det treffer. Ulike lysstråler kan dermed komme fra «alle kanter» med et utall av innfallsvinkler, intensitet og farge.
Det gjøres derfor en del forenklinger for at det skal være mulig å simulere lys på en fornuftig måte. Forenkling er spesielt viktig i spill som kjører en «game loop» og dermed må utføre slike beregninger f.eks. 60 ganger i sekundet.
3D-modeller, lys og shading
Hvilken farge et objekt i den virkelige verden fremstår med er avhengig av lysets farge og retning samt hvilket materiale objektet er laget av. Lyset treffer objektet, det reflekteres og «treffer» våre øyne som, vha. hjernen, oppfatter objektets farge. En boks som belyses med hvitt lys vil fremstå med ulike farger avhengig av hva boksen er laget av og hvilken type lys (spotlys, sol o.l.) boksen belyses med.
I den virkelige verden er det to fenomener som inntrer når et objekt belyses:
- Objektets overflate farge/skyggelegges i forhold til lysets farge og retning samt materialet boksen er laget av.
- Objektet vil kaste skygge avhengig av lyskildens posisjon og retning/orientering.
Det første av disse punktene (1) omhandler det som på engelsk kalles «shading». Begrepet kan oppfattes som noe diffust siden det på norsk direkte oversatt betyr noe sånt som «skyggelegging». I 3D-datamaskingrafikk handler shading om å gi objektenes ulike sider forskjellige farger basert på lysfarge, materiale og lysretning.
Et eksempel: Som vist i figuren over fremstår boksens ulike flater med litt forskjellige fargenyanser avhengig av hvor lyset kommer fra og fra hvilken vinkel boksen betraktes. Boksens sider er ulikt farge/skyggelagt – herav begrepet «shading».
Shading er såpass fundamentalt i forbindelse med 3D-grafikk at språket som man programmerer GPUen/pipelinen med kalles “shading language”. Sammen med WebGL brukes GLSL ES som står for OpenGL ES Shading Language. Se [18], [19] og [20]. Det opprinnelige poenget med verteks og fragment- shadere var å utføre shading, dvs. beregne pikslenes farger vha. lysfarger, lysretning m.m.
Punkt 2 over omfatter det å vise skygge av et objekt. Det er selvfølgelig mulig å generere og vise skygger av 3D-modeller, men dette vil ikke bli omtalt her.
Terminologi
Følgende begreper er sentrale i forbindelse med lys og lysberegning:
- Lokal eller global belysningsmodell.
- Materialegenskaper
- Lysberegning
- Ulike typer lyskilder
- Retningsorientert lys
- Punktlys
- Spotlight
- Ambient lys
- Phong reflection model.
- Fargen til et objekt bestemmes av lyset som reflekteres fra modellen.
- Reflektert lys deles inn i følgende refleksjonskomponenter: Ambient, Diffuse, Specular
- Shading-teknikker: Bestemmer hvor lysberegninga utføres, og hvordan resultatet
interpoleres over trekanten. Varianter:
- Flat shading – én farge for hele trekanten, ingen interpolering
- Gouraud shading – lysberegning per verteks, fargen interpoleres
- Phong shading – normalvektoren interpoleres, lysberegning per fragment
- Flatenormal eller gjennomsnittsnormal per verteks: avgjør om overgangen mellom trekantene blir skarp eller myk. Dette er et valg i modelldataene, uavhengig av shading-teknikken.
- Begrepet shading omfatter det å beregne fargen til et punkt, et primitiv (trekant) eller objekt/modell basert på:
- Lyskildenes posisjon og avstand
- Fargen på lyset
- Materialegenskaper
- Kameraets posisjon
- Normalvektorer
- Invers-transponert modellmatrise brukes til å transformere normalvektorer.
Disse begrepene omtales og forklares i resten av dette notatet.
Lokal eller global lysmodell
I forbindelse med spillutvikling brukes som regel en såkalt lokal belysningsmodell, local illumination model [22], som betyr at hvert objekt belyses uavhengig av hverandre og der man kun tar hensyn til lyset som kommer direkte fra lyskilden. Reflektert lys fra andre modeller ignoreres dermed ved beregning av lys. Dette forenkler beregningene og er illustrert i figuren under. Selv om kula til venstre egentlig skjuler den andre kula får de samme belysning.
Et eksempel på en lokal belysningsmodell er «Phong reflection model» [2][3]. Denne modellen brukes normalt i sanntidsspill og omtales derfor i eget kapittel under.
Alternativt vil man i en global belysningsmodell (global illumination model) også ta hensyn til lys som reflekteres fra alle andre objekter i scenen. Slike modeller er naturlig nok mer beregningskrevende og mindre aktuelle i 3D-sanntidsspill.
Eksempel på en global belysningsmodell er ray tracing som kan produsere fotorealistiske bilder. Siden dette er en beregningskrevende teknikk benyttes dette som regel der bildet «rendres» i god tid før det vises på skjermen som f.eks. til stillbilder og/eller film.
Materialegenskaper
Når vi bruker lysberegning trenger man ikke sette farge på hver enkelt verteks selv om det også er mulig å blande verteksfarge med beregnet lysfarge. Fargene kan i stedet bestemmes ved å spesifisere lys- og materialegenskaper. Verteks- og/eller fragmentfargene beregnes så vha. disse egenskapene.
Eksempler på materialegenskaper er hvilke farger som reflekteres eller absorberes fra en belyst overflate, materialets gjennomsiktighet og evt. gjenskinn. Lys modelleres vha. en blanding av tre farger; rød, grønn og blå (RGB – Red, Green, Blue). Når lys går fra en lyskilde mot et objekt vil noe av lyset reflekteres og noe absorberes. Dersom objektet er gjennomsiktig, for eksempel glass, vil deler av lyset passere gjennom objektet. I den virkelige verden vil også de ulike objektene belyses med reflektert lys fra andre objekter, vegger osv. Det reflekterte lyset ender opp hos betrakteren (øyet). Det menneskelige øye skiller på rødt, grønt og blått lys av varierende styrke. Dette hjelper hjernen å danne et bilde basert på lysreseptorene i øyet.
Figuren viser hvordan hvitt, retningsorientert lys (a), reflekteres fra og absorberes av sylinderen og kula før det treffer øyet. Anta for eksempel at materialet som sylinderen er laget av reflekterer 75 % av rødt lys, 75 % av grønt lys og absorberer resten (b). Alt blått lys vil dermed absorberes. Betrakteren vil dermed oppfatte sylinderen som gul (blanding av rødt og grønt).
Kula reflekterer 25 % (c) av det røde lyset og absorberer resten. Kula vil dermed fremstå som (mørk) rød for betrakteren. I virkeligheten vil også noe lys reflekteres fra sylinderen og treffe kula, og motsatt.
I den virkelige verden er det med andre ord materialet et objekt er laget av, eller kledd/dekket med, som bestemmer hvilket lys, og dermed farge, som reflekteres. En agurk oppfattes som grønn fordi overflaten har fysiske egenskaper som gjør at den reflekterer den grønne delen av lysspektret.
Lys angis ofte som en RGB-verdi, f.eks. [1.0, 1.0, 1.0] for hvitt lys. Det samme gjelder materialegenskapen, f.eks. [0.75, 0.75, 0.0] (for sylinderen i figuren over).
Resultatfargen er en komponentvis multiplikasjon:
farge * materiale =
[1.0, 1.0, 1.0] * [0.75, 0.75, 0.0] =
[1.0 * 0.75, 1.0 * 0.75, 1.0 * 0.0,] =
[0.75, 0.75, 0.0]
som gir gul farge.
Lysberegning
I forbindelse med «shading» av objekter er følgende essensielt:
- Type lyskilde som benyttes.
- Hvordan lyset reflekteres fra flatene til objektet, dvs. hvilken belysningsmodell som brukes.
Ulike typer lyskilder
I 3D-datamaskingrafikk opererer man gjerne med følgende typer lyskilder:
- Retningsorientert lys
- Parallelle innkommende lysstråler, simulerer sollys
- Punktbelysning
- Simulerer for eksempel en lyspære der lyset stråler i alle retninger
- Spotlys / spotlight
- Simulerer en fokusert lyskjegle, eks. lommelykt, gatelykt, billys m.m.
- Ambient
- Indirekte lys / omgivelsesbelysning. Dette er egentlig ikke en kilde i seg selv, men i stedet et resultat av alle andre lyskilder. Ambient lys har ingen retning. Alle modeller og sider belyses likt fra alle kanter.
Figuren under viser forskjellen på disse.
I forbindelse med WebGL simuleres slike lystyper ved å gjøre ulike beregninger i verteks- og/eller fragmentshaderen.
Retningsorientert lys er lys med kilde som vi antar er uendelig langt unna slik at lysstrålene faller parallelt inn mot objektene. Vi kan sammenlikne dette med sollys. Når vi bruker retningsorientert lys bruker vi en «lysvektor» til å angi retninga. Dette betyr at man kan bruke samme vektor for alle verteksene (og/eller fragmentene) til en modell når man gjør lysberegning.
Punktlys har en posisjon og utstråler lys i alle retninger fra dette punktet. Eksempel på punktlys er en lyspære uten skjerm. Innfallsvinkelen mellom lyset og objektet vil variere fra verteks til verteks og lysvektoren må beregnes for hver verteks (evt. fragment).
Et spotlys avgir et kjegleformet lys med en bestemt retning. Eksempler på spotlys er en lampe med skjerm, frontlysene til en bil o.l.
|
|
|
Figuren viser geometrien som man må kjenne til for å kunne beregne og simulere spotlys.
Ambient lys: Dette er en type lys som alltid er til stede i en scene – en form for bakgrunnsbelysning som kommer fra alle kanter og er til stede pga. refleksjoner fra alle andre lyskilder.
Phong reflection model
Tidligere har vi sett hvordan vi kan knytte en fargeverdi (RGBA) direkte til en verteks. På veien mellom verteks- og fragmentshaderen interpoleres verteksfargene slik at hvert fragment til en trekant får farge basert på en interpolert verdi. I stedet for å angi farge for hver verteks kan fargen kalkuleres i verteksshaderen basert på diverse lysparametre som normalvektor, lysretning, lysfarge osv. Dette betyr at verteksshaderen må motta slike verdier/parametre (i tillegg til verteksposisjon) for å kunne beregne verteksfarge. Alternativt kan slike parametre, f.eks. lysvektoren, videresendes (vha. en out-/in-variabel) til fragmentshaderen slik at disse også interpoleres lineært. Hvert fragment har da, eksempelvis, en lysvektor som kan benyttes til å beregne lysfarge for dette fragmentet.
«Phong reflection model» ble utviklet av Bui Tuong Phong og publisert i 1975 [3] og er en lokal belysningsmodell. Her er det viktig å innse at fargen til et objekt bestemmes av fargen til lyset som reflekteres fra modellen. Et rødt objekt fremstår, i den virkelige verden, som rødt fordi overflaten har fysiske egenskaper som gjør at det reflekterer det røde lyset. Modellen, som også kalles «ADS lysmodell», er basert på at resultatfargen til en verteks, eller et fragment, er summen av følgende refleksjonskomponenter:
- Ambient refleksjon (omgivelsesrefleksjon)
- Diffus refleksjon (spredt refleksjon) og
- Specular refleksjon (speilrefleksjon)
Resultatfargen (totalrefleksjon) til et punkt, verteks eller fragment, kan beskrives som summen av de nevnte refleksjonskomponentene, slik:
Totalrefleksjon = ambient refleksjon + diffus refleksjon + speilrefleksjon
For å kunne beregne disse tre komponentene brukes følgende tre lyskilder:
- Ambient lys (omgivelseslys),
- Diffus lys («spredt»/diffust lys) og
- Specular lys («speilglanslys»).
I tillegg angis «materialet» som objektene består av vha.
- ambient,
- diffus og
- specular materialegenskaper
Ambient refleksjon, i likninga over, beregnes med andre ord vha. ambient lys og ambient materialegenskap. Ambient refleksjon tilsvarer da fargen som ambient lys og ambient materialfarge til sammen utgjør. Disse slås sammen vha. komponentvis multiplikasjon, som vist over. Tilsvarende for diffus og specular refleksjon. Diffus refleksjon tilsvarer fargen som diffus lysfarge og diffus materialfarge til sammen utgjør mens speilrefleksjon tilsvarer fargen som specular lysfarge og specular materialfarge til sammen utgjør.
Her er det mange begreper ute og går – det som er viktig er at «ambient refleksjon» omfatter lys og materialegenskaper. Begge disse angis som en fargevektor bestående av en rød, en grønn og en blå-komponent (RGB). Det betyr at både lys og materialegenskap angis som RGB-verdier, dvs. 3 flyttallverdier i området 0 - 1. Disse slås så sammen vha. komponentvis multiplikasjon.
Eksempel : Anta ambient lys = [1,1,1] og at dette belyser et objekt med materialegenskap = [0.4, 1.0, 0.0]. Ambient refleksjon blir da produktet av disse, som vil bli [0.4, 1.0, 0.0]. Dette indikerer at 40 % av innkommende rødt lys reflekteres mens 60 % absorberes. Alt grønt lys reflekteres mens alt blått lys absorberes. Ofte angis et slikt lys som en lysvektor, altså som en sammenslått verdi (og ikke både lys og materialvektor). I dette tilfellet [0.4, 1.0, 0.0].
Ambient refleksjon
Ambient lys, eller omgivelsesbelysning, er lys som alltid er til stede i en scene - en form for bakgrunnsbelysning som kommer fra alle kanter. Dette er uavhengig av objektenes orientering og plassering i forhold til andre lyskilder. Alle deler av modellen belyses likt med dette lyset.
For å kunne beregne ambient refleksjon angis ambient lys, Ia, og ambient materialegenskap, ka. Disse slås sammen og gir ambient refleksjon. Ambient refleksjon = ka * Ia
Ambient refleksjon tilsvarer da fargen som utgjøres av omgivelsesbelysning og materialegenskapen til objektet som belyses. Dette implementeres for eksempel ved å sende disse to verdiene til verteksshaderen, som slår disse sammen:
uniform vec4 uAmbientMaterial; uniform vec4 uAmbientColor; . . . vec4 ambientReflection = uAmbientMaterial * uAmbientColor;
Denne verdien, ambientReflection, summeres så typisk med tilsvarende for «diffuse» og «specular»-komponentene. Beregnet farge sendes deretter videre til fragmentshaderen via en out-/in-variabel (slik at fargen interpoleres). Verteks- og fragmentshader som kun håndterer ambient-lys vil kunne se slik ut:
Legg merke til at verteksshaderen tar inn posisjon (aVertexPosition) men ingen verteksfarge. Fargen beregnes her ved å slå sammen uniform-parametrene uAmbientMaterial og uAmbientColor vha. en komponentvis multiplikasjon.
Bruk av en verdi for ambient refleksjon
I praksis opererer man ofte med en fargeverdi (vektor) for ambient refleksjon, en for diffus refleksjon og en for specular refleksjon. Vi kan f.eks. slå sammen lys- og materialfarge i Javascript-koden, evt. bare bruke en verdi og anta at dette er sammenslått lys og materialfarger, før den sendes til shaderen. Foreløpig ser vi kun på ambient og følgende kode viser bruk av en fargeverdi for ambient refleksjon. Her kaller vi den uAmbientLightColor. Shaderne blir da enda enklere (out-variabelen er nå navngitt vLight):
I javascript-koden må uAmbientLightColor gis verdi på samme måte som vist tidligere. Bruk av kun ambient lys vil f.eks. kunne gi følgende resultat (dersom uAmbientLight er tilordnet en gråfarge):
Kuben belyst med kun omgivelsesbelysning. Som vi ser er 3D-effekten fraværende. Siden alle sider belyses likt vil alle fragmenter få samme fargeverdi. Ambient lys alene vil ikke gi tilfredsstillende 3D-resultat. Dette brukes kun til å heve totalbelysningen. Husk at totalbelysning(/refleksjon) beregnes slik: Totalrefleksjon = ambient refleksjon + diffus refleksjon + specular refleksjon. Vi har i foregående eksempel kun beregnet første del av denne likninga. I neste avsnitt ser vi på diffus refleksjon.
Diffus refleksjon
Diffust lys er lys som har en retning, dvs. som kommer fra en lyskilde. Dette kan være retningsorientert lys eller punktlys. Ved beregning av diffus refleksjon må man ta hensyn til innfallsvinkelen til lyset. Lys som står vinkelrett mot en flate vil reflektere mer lys enn lys som faller skrått mot en flate.
Til venstre i figuren er dette illustrert vha. et punktlys. Her er det indikert at lys som treffer vinkelrett på flaten gir en mer opplyst flate enn lysstråler som treffer med innfallsvinkel ulik 90 grader. Det samme er tilfelle for retningsorientert lys, stråler med innfallsvinkel ulik 90 grader gir en svakere belyst flate.
Ved diffus refleksjon vil det reflekterte lyset spres likt i alle retninger. Dette betyr igjen at resultatfargen er uavhengig av hvor kameraet står, dvs. hvor objektet betraktes fra. Som vi etter hvert vil se har kameraets posisjon derimot avgjørende betydning i sammenheng med speilrefleksjon (specular reflection). Matte og bleke materialer som leire, sand, kalk o.l. gir diffus refleksjon.
Før vi går videre og beregner diffust lys må vi diskutere normalvektoren.
Normalvektor
Figuren under illustrerer hvordan belysningen på en flate varierer med innfallsvinkelen til lyset. Vi ser her et rektangel, tegnet vha. to trekanter, som gradvis retter seg opp mot lyset.
Når rektanglet ligger flatt («på bakken») vil ikke noe lys treffe flaten og den vil derfor vises som mørk. Etter hvert
som rektanglet retter seg opp slik at lyset treffer bedre vil flaten fremstå med klarere og klarere farge. Strekene som
peker ut fra rektanglet indikerer normalvektorer knyttet til verteksene. Lyset treffer altså flatene med ulike
innfallsvinkler.
Figuren under viser hvordan lyset treffer tre ulike flater (sett ovenfra) og hvordan normalvektoren er med på å bestemme
hvor mye av lyset som skal reflekteres.
Dersom lyset skinner mot de tre flatene skal flate nr 1 reflektere mer lys enn flate 2 og 3. De røde linjene indikerer hvor mye lys som reflekteres fra de ulike flatene. Vi ser at den røde linja fra flate 3 er mindre enn tilsvarende fra flate 1 og 2. Størrelsen på disse (tenkte) linjene kan beregnes dersom WebGL får opplysning om de vinkelrette pilene – normalvektorene – som peker fra hver flate.
Normalvektorene knyttes, på samme måte som verteksfarger, til verteksene. Hver trekant vil dermed bestå av tre verteksposisjoner og tilhørende normalvektor. For å kunne beregne diffus refleksjon må man med andre ord ha normalvektoren til flaten som verteksen er med på å definere. Normalvektoren er i enkelte tilfeller enkel å beregne mens i andre tilfeller er dette mer krevende.
Lamberts lov
Diffus refleksjon kalles også «Lambertian reflection» pga. bruk av Lamberts cosinus-lov (Wikipedia/Lamberts cosine law, [22]) som forklares her. Lys som treffer en overflate direkte (vinkelrett) vil belyse flaten mer enn lys som treffer flaten med en vinkel. Figuren under illustrerer dette.
Flaten mottar mest lys når lysvektoren L og normalvektoren n er parallelle – dvs. lyset skinner direkte på flaten. Flaten mottar mindre lys jo større vinkel det er mellom disse vektorene. Poenget er å finne en funksjon som returnerer ulik intensitet basert på vinkelen mellom verteksnormalen (n) og lysvektoren (L). Legg merke til at vektoren L peker i motsatt retning i forhold til lysets retning. Funksjonen må returnere maks intensitet når n og L er parallelle dvs. vinkelen mellom dem er lik 0. Intensiteten skal avta etter som vinkelen øker. Når vinkelen passerer 90 grader belyses baksiden og intensiteten må da settes lik 0. Lamberts cosinus-lov gir oss dette:
\[ f(\theta) = \max(\cos\theta,\; 0) = \max(\vec{L} \cdot \vec{n},\; 0) \]Der både L og n er enhetsvektorer (normalisert). Her brukes prikk (dot) produktet for å finne vinkelen mellom vektorene L og n. Generelt beregnes prikkproduktet mellom to vektorer \(\vec{u}\) og \(\vec{v}\) slik:
\[ \vec{u} \cdot \vec{v} = \left\lVert \vec{u} \right\rVert \left\lVert \vec{v} \right\rVert \cos\theta \]I dette tilfellet tilsvarer u og v henholdsvis L og n. Så lenge L og n er enhetsvektorer (normalisert) er cosθ lik prikkproduktet mellom disse:
\[ \cos\theta = \frac{\vec{u}}{\left\lVert \vec{u} \right\rVert} \cdot \frac{\vec{v}}{\left\lVert \vec{v} \right\rVert} \]L settes typisk lik normalisert lysvektor multiplisert med -1.
Beregne normalvektor: flatenormal og verteksnormal
En flatenormal er en enhetsvektor som står vinkelrett på et polygon (en flate) og dermed beskriver polygonets retning.
Flatenormalen til en trekant tilsvarer normalisert kryssprodukt mellom to av trekantens «kantvektorer». Dette gir en vektor med lengde = 1 som står vinkelrett på trekanten. Husk at rekkefølgen på kryssproduktet bestemmer retningen på normalen.
Anta en trekant beskrevet av tre punktvektorer, p0, p1 og p2 som vist over. Normalvektoren i p0 kan beregnes slik:
Vektorene u og v beregnes slik:
\[ \begin{aligned} \vec{u} &= \overrightarrow{p_1} - \overrightarrow{p_0} \\ \vec{v} &= \overrightarrow{p_2} - \overrightarrow{p_0} \end{aligned} \]
Vi bruker deretter kryssproduktet til å beregne en normalvektor til flaten (trekanten) definert av disse vektorene. Resultatet normaliseres, slik at lengden til n blir 1:
\[ \vec{n} = \frac{\vec{u} \times \vec{v}} {\left\lVert \vec{u} \times \vec{v} \right\rVert} \]
n er nå en normalisert normalvektor for trekanten. Denne vektoren kan nå tilordnes som normalvektor til verteksene som definerer trekanten. Problemet med å beregne normalvektor på denne måten i et større mesh er at overgangene mellom de ulike trekantene vil vises tydelig. Grunnen er at alle piksler innafor samme trekant får samme normalvektor, og farge, og man får dermed et skarpt fargeskille mellom hver enkelt trekant. Dette kalles flat shading.
For å få en jevnere belysning/refleksjon («shading») kan normalene beregnes per verteks i stedet for per trekant. Som vi skal se etter hvert kan man også gjøre lysberegning per fragment. Når normaler beregnes per verteks tar man hensyn til flatene som deler samme verteks.
Figuren viser et mesh bestående av 4 trekanter og hvordan disse deler en av verteksene. Normalvektoren, v, er gjennomsnittet av flatenormalene for de 4 trekantene. Vektoren v23 er her beregnet vha. vektorene v2 og v3, v34 er beregnet av vektorene v3 og v4 osv. Brukes nå vektoren v som normalvektor i den delte verteksen for alle de fire trekantene får man en jevnere belysning. Et annet eksempel : Anta at vi har to flater (sett fra siden) som belyses som vist under.
Dersom verteksene til flatene bruker flatenormalene vil det oppstå et klart fargeskille på flatene som vist til venstre i figuren (a). Dette kalles, som nevnt, flat shading. Dersom man i stedet beregner og bruker gjennomsnittsnormalvektorer på de verteksene som er delt av flatene vil fargene få en jevnere overgang. Dette ser vi i høyre del av figuren (b). Dette kalles Gouraud eller Phong shading avhengig av om lysberegninga utføres i verteks- eller fragmentshaderen. Mer om dette etter hvert. Figuren under viser flatene ovenfra:
|
|
|
Noen ganger ønsker man å bruke flatenormalene uten å beregne gjennomsnittet mens i andre tilfeller må man beregne gjennomsnittet for at resultatet skal bli brukbart. Et eksempel er en kule bestående av mange trekanter. Dersom man ikke bruker gjennomsnittsnormalvektorer vil kulens trekanter bli veldig synlige, brukes gjennomsnittsnormalvektorer får kulen en jevnere overflate.
Ved å gjennomløpe alle trekanter til en modell (et «mesh») og beregne flatenormalen (som forklart over) til hver enkelt trekant kan gjennomsnittsnormalvektoren beregnes i delte vertekser. Hver verteks tilordnes så beregnet gjennomsnittsnormalvektor. Se [30].
Dersom man skal tegne en kube vil det som regel være greit å bruke flatenormalen, som står 90 grader på hver side, til sidenes vertekser. Man trenger i dette tilfellet ikke beregne gjennomsnittsnormalvektor. Se neste avsnitt.
Normalvektorer til en kube
En kube med skarpe kanter vil ha klare fargeforskjeller på sidene. Det er derfor enkelt å finne normalvektorene til kubens sider – disse står vinkelrett på hver side.
Her vil det være 6 forskjellige normalvektorer som står vinkelrett på flatenes sider. Korrekt normalvektor knyttes til hver enkelt verteks som definerer kuben. Dersom f.eks. høyre side av kuben er definert av 4 vertekser, assosieres disse verteksene med flatenormalen til kubens høyre side (som i dette tilfellet vil være [1,0,0]).
Figuren viser at det må knyttes en normalvektor, her flatenormalen til hver side, til hver enkelt verteks.
Knytte normalvektorer til verteksene
Tidligere har vi sett hvordan man assosierer farge med vertekser ved at det opprettes et eget fargebuffer som fylles med fargedata, i form av en RGBA-verdi, per verteks. På samme måte må man assosiere en normalvektor per verteks for å kunne gjøre lysberegninger. Dersom vi tar utgangspunkt i en kube vil verteksposisjoner f.eks. kunne defineres slik:
Tilsvarende verteksnormaler defineres f.eks. slik:
Posisjon- og normalvektorbuffer opprettes slik:
Her opprettes et buffer for verteksposisjonene og et buffer for verteksnormalene. Dette tilsvarer det som har vært gjennomgått tidligere. Kuben defineres vha. to trekanter per side, hver trekant består av 3 vertekser som totalt gir 36 vertekser. Husk at det må være like mange normalvektorer som posisjoner, dvs. 36 normalvektorer. Verteksposisjonene og verteksnormalverdiene sendes til shaderen via tilsvarende shaderparametre. Det vil si at verteksshaderen, for hver verteks, forventer posisjonsdata og normalvektor. En komplett shader kan f.eks. se slik ut:
Shaderen er ikke komplett, den mangler selve lysberegninga. Som vi ser inneholder den flere lys-relaterte parametre som vil bli brukt til å beregne lys. Dette vil bli forklart etter hvert. Eksempelkoden under viser hvordan vi kopler shaderparametrene til variabler/objekter i Javascriptkoden:
I drawCube(), som kalles fra draw(), sendes lysverdiene inn til shaderen:
Legg merke til normalMatrix som opprettes i drawCube() - denne har vi ikke brukt før og krever en forklaring. Verteksposisjonene transformeres i verteksshaderen vha. modelview- og projeksjonsmatrisene, som før. Normalvektorene må også transformeres. Normalvektoren representerer en retning og translasjon (forflytning) av denne blir meningsløs. Bruker man modellmatrisa til å transformere normalvektoren og denne inneholder translasjon vil normalvektoren kunne få feil retning. Dersom modellen roterer må normalvektoren rotere tilsvarende. «Uniform» skalering, dvs. lik skalering i alle akser, vil ikke endre normalvektorens retning (kun lengde). «Skeiv» skalering, dvs. ulik skalering i de ulike aksene, vil endre retninga til normalvektoren.
Poenget er at man ikke uten videre kan bruke modellmatrisa (eller modelview-matrisa) til å transformere normalvektorene. For å løse dette bruker vi en egen normalmatrise, som tilsvarer invertert og transponert øvre-venstre 3x3 submatrise av modellmatrisen (evt. modelview-matrisen), til å transformere normalvektorene. Normalmatrisen genereres typisk i Javascript-koden og sendes til verteksshaderen som mottar denne via en uniform-variabel (her: uNormalMatrix), på samme måte som den mottar modelview- og projeksjonsmatrisene.
Funksjonen mat3.normalFromMat4() (som ligger i gl-matrix.js biblioteket glMatrix ) sørger for å fjerne evt. translasjon (forflytning) som måtte ligge i modellmatrisa. Dette gjøres ved å redusere 4x4 modellmatrise til en 3x3 matrise som tilsvarer øvre venstre 3x3-del av modellmatrisa (translasjon ligger i høyre kolonne i modellmatrisa, som altså fjernes). Denne 3x3 matrisa inverteres og transponeres. Resultatet er en normalmatrise , dvs. en matrise som kan brukes til å transformere normalvektorene.
Retningsorientert lys og diffus refleksjon
Shaderen har nå tilstrekkelig informasjon til å kunne beregne diffus refleksjon når et objekt belyses med retningsorientert lys («sola»), med en gitt retning. For å kunne beregne diffus refleksjon trenger vi, som tidligere nevnt, diffust lys, Id, og diffus materialegenskap, kd. I tillegg må man ta hensyn til lysets retning slik at lysets intensitet avtar med fallende innfallsvinkel - her brukes derfor Lamberts lov:Diffus refleksjon = kd * Id * max(cosθ, 0)
Diffus refleksjon tilsvarer da fargen som utgjøres av diffus belysning (Id) og diffus materialegenskap (kd) til objektet som belyses. θ er vinkelen mellom normalvektoren og lysvektoren. Som indikert i avsnittet om ambient refleksjon er lys og materialegenskapene ofte slått sammen slik at man opererer med en verdi, slik: Diffus refleksjon = diffust_lys * max(cosθ, 0) der diffust_lys f.eks. er lik: [0.1, 0.8, 0.3] (som representerer en nyanse av grønn). Se kodeeksemplene for demo av diffus refleksjon sammen med retningsorientert lys mot både kube og kule. Her brukes også ambient lys for å heve den generelle belysninga noe. Tilsvarende for punktlys omtales i neste delkapittel. Komplett shader som brukes til å beregne lys og farge ser slik ut:
Shaderen mottar posisjon og normalvektor for hver verteks – begge av type vec3. Lysberegning utføres dermed per verteks
- dvs. i verteksshaderen. Ellers mottas normalmatrisen i tillegg til modelview- og projeksjonsmatrisene (som uniform
mat3 og uniform mat4).
Lysberegninga er basert på at lyskilde og normalvektor er i world-koordinater. Det betyr at det forutsettes at
lysretning angis (direkte) i forhold til world-koordinatsystemet. Normalvektoren transformeres i shaderen vha.
normalmatrisen for å få den i world-koordinater.
Diffus lysfarge, diffuseLightWeighting, for aktuell verteks, beregnes vha. normalisert lysvektor (vectorToLightSourceNorm), transformert normalvektor (normal) og Lamberts lov. Verdien til cosθ i Lamberts lov tilsvarer prikkproduktet mellom normalvektoren og lysvektoren. Resultatet tilordnes diffuseLightWeighting.
Out-variabelen vLightWeighting er «komplett» lysfarge for aktuell verteks. Denne er basert på ambient og diffuse lysverdier. vLightWeighting interpoleres på veien til fragmentshaderen der den mottas (nå som en in-variabel). OpenGL ES-variabelen gl_Position tilordnes som tidligere forklart. Tilhørende fragment-shader ser slik ut:
Her mottas interpolert verdi til vLightWeighting som ble satt i verteksshaderen. Siden dette er en vec3 tilordnes fragColor en vec4-vektor der alpha-verdien settes lik 1.0.
Resultatet blir omtrent som følger når lysvektoren = [1,0,0], dvs. lyset lyser mot negativ x (vektoren oppgis motsatt i forhold til lysretninga). Her er ambient lys = [0.2, 0.2, 0.2] og diffust lys = [0.1, 0.8, 0.3]. Kameraet er plassert i [25, 60, 100]:
Som vist i figuren får kuben farge kun på den siden som lyset lyser på. De andre sidene er nesten svart, men er allikevel svakt belyst pga. ambient-lyset. I figuren er posisjon og retning til lyskilden og kameraet indikert (plassering/posisjon er ikke i riktig skala). I Beregning av ambient og diffus lysrefleksjon kontrolleres det at det vi ser på skjermen stemmer med teorien. Legg ellers merke til at kubens sider har samme farge uansett hvilken vinkel vi ser kuben fra - fargen er uavhengig av kameraets plassering.
Endring av lysets retning
Dersom vi endrer på lysets retning til [-10, 6.0, 10.0] får vi følgende:
Husk at ambient og diffus-fargene blandes og intensiteten avhenger av lysets innfallsvinkel. Her ser vi tydelig hvordan «shading» utarter seg. Toppen av kuben blir litt mørkere enn fronten siden lysets innfallsvinkel mot toppen er mindre enn mot fronten. Høyre side av kuben er ikke belyst av diffust lys, kun ambient og er derfor nesten helt svart.
Punktlys og diffus refleksjon
Når retningsorientert lys brukes, som gjennomgått i forrige avsnitt, antas det at lyskilden er så langt unna at lysstrålene faller parallelt mot modellen – retningsorientert lys er egentlig et punktlys i uendelig avstand fra modellen.
I dette avsnittet ser vi på punktlys. Dette er en lyskilde som har en posisjon (dvs. i endelig avstand fra modellen). Siden lyskilden har en posisjon må lysvektoren beregnes for hver verteks.
Lysvektoren
beregnes ved å ta differansen mellom lyskildens posisjon og verteksposisjonen. Her er det viktig at verteksen og
lyskilden er i samme koordinatsystem. I denne gjennomgangen brukes world-systemet, som er enklest og mest naturlig.
Verteksen er i «world» koordinatsystemet etter at verteksen er multiplisert med modellmatrisa. Lyskildens posisjon
oppgis direkte i world-koordinater. For å kunne beregne lysvektoren i shaderen må modellmatrisa sendes til
shaderen (i tillegg til de andre matrisene).
Verteksshaderen ser nå slik ut:
Verteksposisjonen transformeres vha. modell-matrisa til vektoren vertexPosition. Denne brukes kun til lysberegning. Lysvektoren, vectorToLightSource, settes lik differansen mellom lyskildens posisjon, uLightPosition, og transformert verteksposisjon. Denne normaliseres også. Deretter transformeres normalvektoren som videre brukes til å beregne fargen som utgjøres av diffust lys. Verdien legges i diffuseLightWeighting. Variabelen vLightWeighting (og det resterende av shaderkoden) settes på samme måte som i forrige eksempel. Dette er illustrert i figuren under:
For at det skal være mulig å beregne lysvektoren må posisjonen til lyskilden sendes inn til verteksshaderen på samme måte som vi sendte inn andre uniform-parametre. I tillegg må modellmatrisa og normalmatrisene sendes til shaderen. Fragmentshaderen tilsvarer også det som ble brukt i forrige eksempel:
Oppsummering diffust lys / refleksjon
Vi har nå gjennomgått de to første leddene i likninga: Totalrefleksjon = ambient refleksjon + diffus refleksjon + specular refleksjon. Du finner komplett kode blant kodeeksemplene under modul5. Det siste leddet i likninga omhandler speilrefleksjoner, men først litt mer om ulike typer shading.
Ulike typer shading
Det er to uavhengige valg som til sammen avgjør hvordan en modell blir «shadet»: hvilken normalvektor verteksene får, og hvor lysberegninga utføres. I tillegg vil resultate avhenge av hvordan interpolering utnyttes. Lysberegning kan, uavhengig av hvordan normalvektoren er beregnet, utføres i verteks- eller i fragmentshaderen. De tre teknikkene vi omtaler under er kombinasjoner av disse to valgene:
| Teknikk | Normalvalg | Lysberegning | Interpolering |
|---|---|---|---|
| Flat shading | flatenormal | per verteks | ingen (flat) |
| Gouraud shading | gjennomsnittsnormal | per verteks | fargen interpoleres |
| Phong shading | gjennomsnittsnormal | per fragment | normalvektoren interpoleres |
Flat shading
En 3D-modell består av mange trekanter. Hver trekant defineres av tre vertekser. For lysberegning må man knytte en normalvektor til hver verteks. Som tidligere forklart kan denne enten være lik trekantens flatenormalvektor eller et gjennomsnitt av tilstøtende trekanters flatenormaler. Når man bruker flatenormaler på hver enkelt trekant, i stedet for gjennomsnittsnormalvektorer, kaller vi det flat shading. Alle tre verteksene i trekanten får da samme normalvektor, og med retningsorientert lys blir lysberegninga dermed identisk i de tre verteksene. Interpolasjonen har ingenting å interpolere mellom, alle fragmentene i trekanten får samme fargeverdi, og hver enkelt trekant blir tydelig synlig. De små strekene i figuren under illustrerer flatenormalene til hver trekant.
Dersom vi bruker flatenormalene til å gjøre lysberegning vil skillet mellom trekantene vises tydelig som vist i figuren under.
Her er det benyttet retningsorientert diffust lys (med lilla farge). Jo større trekantene er jo verre blir resultatet. Som regel ønsker man i stedet å bruke gjennomsnittsnormalvektoren til å gjøre lysberegninger. Lysberegning kan, uavhengig av hvordan normalvektoren er beregnet, utføres i verteks eller i fragmentshaderen. Dersom vi gjør lysberegning i verteksshaderen kalles det "Gouraud shading". Lysberegning i fragmentshaderen kalles "Phong shading".
Flat shading med flat-kvalifikatoren
Legg merke til at man oppnår flat shading så lenge verteksene til hver enkelt trekant har samme normalvektor og alle de alle de andre størrelsene i lysberegninga også er like i de tre verteksene. Med punktlys varierer lysvektoren fra verteks til verteks, og med speilrefleksjon varierer i tillegg view-vektoren. I disse tilfellene får fragmentene ulik farge selv om normalvektoren er den samme, og resultatet er ikke lenger flat shading.
I GLSL ES 3.00 kan vi i stedet slå av interpolasjonen direkte, med interpoleringskvalifikatoren flat:
//Verteksshader - lysberegning som før, men verdien skal ikke interpoleres: flat out vec3 vLightWeighting; //Fragmentshader: flat in vec3 vLightWeighting;
Da får alle fragmentene i trekanten verdien fra trekantens «provoking vertex» [58] – for gl.TRIANGLES den
siste av trekantens tre vertekser. Resultatet er flat shading uansett hvilken lystype som brukes. Merk at
kvalifikatoren krever #version 300 es (dvs. WebGL2).
Gouraud shading
Gouraud shading kalles også per-verteks shading siden lysberegninga utføres per verteks og resultatet interpoleres for hvert fragment som inngår i trekanten. Dette betyr at man opererer med (i praksis beregner) normalvektorer for hver enkelt verteks. Det er stort sett denne teknikken vi har benyttet i eksemplene hittil. Dersom man bruker gjennomsnittsnormalvektoren og Gouraud shading til å tegne planet får vi følgende:
Vi ser nå at skillet mellom trekantene er borte. Generelt vil Gouraud shading gi et bra resultat på matte flater. Skinnende overflater, dvs. bruk av speillys, kan gi ulike «artefakter» siden speillys kun beregnes per verteks. Man kan risikere å miste speillysberegninger som faller mellom verteksene. Bruk av Gouraud shading sammen med punktlys kan, i enkelte tilfeller, gi feil belysning. Dette kan unngås ved å bruke Phong shading.
Phong shading
Man kan gjøre all lysberegning per fragment – dette kalles Phong shading (som ikke er det samme som Phong reflection model). Dette kalles også per-fragment-shading. Dette gir normalt det beste resultatet, spesielt for skinnende overflater. Selv uten bruk av speillys vil Phong shading i mange tilfeller være nødvendig for å få et brukbart resultat. Anta at vi har et punktlys som lyser på en rektangulær flate, bestående av to trekanter.
Ifølge det vi har sett på tidligere beregnes diffust lys/intensitet i henhold til følgende formel:
\[ \text{diffus intensitet} = \max(\cos\theta,\; 0) = \max(\vec{L} \cdot \vec{n},\; 0) \]Dette skulle tilsi at lyset i punkt p0 skal være mest intenst (cos(0°) = 1). I verteks v0 og v1 er innfallsvinkelen større som betyr lysets intensitet skal være mindre (cos(60°) = 0.5). Dersom Gouraud shading brukes i dette tilfellet vil ikke midten av planet bli mest belyst. Her vil lysberegning kun utføres i v0 og v1 og lysfargen ved disse verteksene blir mindre intens. Fragmentene (dvs. bl.a. p0) mellom disse verteksene får interpolerte verdier og vil også fremstå med svakere intensitet. Resultatet blir derfor ikke tilfredsstillende i dette tilfellet.
Dersom Phong shading benyttes sendes normalvektoren knyttet til hver verteks videre til fragmentshaderen slik at all lysberegning i stedet kan gjøres her – dvs. per fragment. Normalvektoren sendes som en out-/in-variabel til fragmentshaderen slik at fragmentshaderen mottar en (lineær)interpolert normalvektor. I forhold til eksemplet med en belyst flate vil det nå utføres en egen lysberegning i p0 slik at dette punktet får korrekt belysning (dvs. mest intenst lys).
Verteks- og fragmentshaderene under viser dette:
Phong shading er den mest GPU-krevende teknikken siden beregninger utføres per fragment i stedet for per verteks. Det kan i enkelte tilfeller være vanskelig å se forskjell på bruk av Gouraud og Phong-shading.
Speilrefleksjon (specular reflection)
Det siste leddet i likninga Totalrefleksjon = ambient refleksjon + diffus refleksjon + specular refleksjon omhandler specular lighting, eller speilrefleksjon. Bruk av ambient og diffus refleksjon gjør at modeller fremstår med 3D-effekt. Overflaten til modellene vil imidlertid være «matte» (som kalk). I dette kapitlet skal vi se hvordan 3D-modeller også kan ha glatte og skinnende overflater. Dersom man bruker specular belysning sammen med ambient og diffus belysning vil ulike deler av modellen kunne bli skinnende (dvs. med annen/lysere farge enn diffus/ambient-fargen). Hvilken del av modellen som skinner vil avhenge av hvilken vinkel modellen betraktes fra, dvs. kameraets plassering er avgjørende. En rød kule som belyses med hvitt lys vil, avhengig av kameraplassering, få en lys skinnende flekk som oppfattes som om kula er glatt og skinnende.
Når vi bruker speilbelysning vil lyset reflekteres fra modellens overflate på samme måte som lys reflekteres fra et speil. Mesteparten av lyset reflekteres i en spesifikk retning og kameraets plassering er derfor avgjørende. Geometrien for speilrefleksjon kan illustreres slik:
Figuren viser de ulike vektorene som er involvert for å kunne beregne speilrefleksjon. Lysvektoren, l, indikerer retningen til lyskilden. Normalvektoren, n, står vinkelrett på flaten. Dersom flaten er veldig reflekterende vil mesteparten av lyset reflekteres i retning av refleksjonsvektoren r. Mindre skinnende flater gjør at lyset spres mer rundt vektoren r. Vektoren v peker i retning av kameraet. Kameraet vil motta mest reflektert lys når vinkelen mellom v og r er null. Ettersom vinkelen mellom v og r øker vil kameraet motta mindre og mindre lys. Denne reduksjonen beregnes slik: \(\max(\cos\theta,\; 0)^{\alpha}\), dvs. prikkproduktet mellom v og r opphøyd i α, der α indikerer graden av gjenskinn (shininess). Verdien til α påvirker hvor raskt intensiteten avtar etter som vinkelen mellom r og v øker. En høy verdi på α gir mye refleksjon og betyr at reduksjonen i intensitet vil skje raskere etter som vinkelen mellom r og v øker. Lavere verdi på α indikerer mindre gjenskinn og betyr at reduksjonen vil skje mer gradvis etter som vinkelen mellom r og v øker. Se (Anyuru, A., 2012) for mer informasjon om dette. Speilrefleksjon beregnes slik:
\[ \text{speilrefleksjon} = \text{speillys} \cdot \max(\vec{r} \cdot \vec{v},\; 0)^{\alpha} \]Refleksjonsvektoren r beregnes slik:
\[ \vec{r} = 2\,(\vec{l} \cdot \vec{n})\,\vec{n} - \vec{l} \]Vi slipper imidlertid å beregne denne vektoren manuelt siden GLSL har en innebygd funksjon, reflect(), som beregner denne vektoren. NB! reflect()-funksjonen forutsetter at lysvektoren peker i samme retning som lyset, dvs. i motsatt retning av det som er angitt i figuren over. Når vi bruker reflect() snur vi lysvektoren ved å sette minus foran.
Eksempel, utsnitt fra verteksshaderen:
. . . //Beregn refleksjonsvektoren, r, for specular belysning: vec3 reflectedVector = normalize(reflect(-lightDirection, normalVector)); . . .
Her antar vi at lightDirection er vektoren mellom lyskilde og aktuell verteks, beregnet på samme måte som tidligere.
Komplett verteks- og fragmentshader kan nå se slik ut:
Det som har med speilbelysning å gjøre står under kommentaren // SPECULAR LYS:. Dersom prikkproduktet mellom
normalvektoren og lysretninga er mindre enn 0, dvs. at vinkelen mellom dem er større enn 90 grader, belyser ikke lyskilden
flata og det er da heller ingen speilbelysning. Ellers beregnes refleksjonsvektoren
vha. reflect()-funksjonen som vist over. Basert på dette beregnes så specular reflection.
Legg merke til at vLightWeighting utgjør summen av ambient, diffus og specular refleksjon. vLightWeighting er en out-variabel og vil interpoleres på veien fra verteksshaderen til fragmentshaderen. I fragmentshaderen defineres en tilsvarende in-variabel. Komplett fragmentshader ser slik ut:
Dette kalles Gouraud shading siden lysberegninga utføres av verteksshaderen. Alternativet er å videresende normalvektoren som en out-/in-variabel til fragmentshaderen slik at vi får en interpolert normalvektor per fragment. Resterende lysparametre sendes som uniforms direkte til fragmentshaderen. Fragmentshaderen kan nå gjøre lysberegning per fragment. Dette kalles Phong shading, som er omtalt tidligere i notatet. Du finner komplett kode blant kodeeksemplene under modul5.
Belysning av kulemodell
Dersom vi har en kulemodell med tilhørende vertekser og normalvektorer kan vi gi denne farge kun ved bruk av lysparametrene som vist i foregående eksempler. Det er relativt greit å «håndkode» alle vertekser og trekanter som trengs for å lage en kule. Vi må også beregne normalvektor per verteks. Alternativt kan man lage en kule vha. et 3D-verktøy og få verktøyet til å gjøre beregningene av normalvektorene – se Noen tips i forhold til Blender og eksport (threejs). Normalvektoren for verteksene til en kule er enkel å «beregne». Denne vil, for hver verteks, være en vektor som peker fra origo til verteksen så lenge kula er sentrert om origo (og normalvektorene også normaliseres).
Du finner komplett kode blant kodeeksemplene under modul5. Resultatet blir f.eks. som vist i figuren under.
Her er «shininess»-parameteret i shaderen satt lik 2.
Blande lys, verteksfarge og tekstur
Blande verteksfarge og lys
Dersom man knytter farge til verteksene vil fargen interpoleres over trekantenes fragmenter. Vi har også sett at vi kan gi modeller farge ved hjelp av diverse lysparametre. Det er også mulig å blande verteksfarger med lys. Verteksshaderen må da, i tillegg til posisjon og normalvektor, også ta imot verteksfarge og blandes med lysfargen. Fragmentshaderen endres som følger:
. . .
in vec3 vColor;
out lowp vec4 fragColor;
. . .
void main() {
. . .
//Summer alle refleksjonskomponenter:
vec3 lightWeighting = uAmbientLightColor + (uDiffuseLightColor * diffuseLightWeighting);
//Multipliserer inn vColor:
fragColor = vec4(lightWeighting.rgb * vColor.rgb, 1.0);
}
Her vil beregnet lysfarge (lightWeighting) blandes med interpolert verteksfarge (vColor).
Blande teksturer og lys
Teksturering av 3D-modeller er med på å øke 3D-realismen. Det samme gjelder belysning og spesielt med bruk av speilbelysning. Dersom vi nå blander disse elementene, dvs. at vi belyser en teksturert modell vil 3D-effekten kunne bli enda bedre.
Dette er egentlig ganske enkelt. Lysberegning gjøres som vist i eksemplene over. I fragmentshaderen blandes lysfarge og teksturfarge som vist under:
. . .
in highp vec2 vTextureCoordinate;
uniform sampler2D uSampler;
out lowp vec4 fragColor;
. . .
void main() {
. . .
//Summer alle refleksjonskomponenter:
vec3 lightWeighting = uAmbientLightColor + (uDiffuseLightColor * diffuseLightWeighting);
fragColor = vec4(lightWeighting.rgb * texture(uSampler, vTextureCoordinate).rgb, 1.0);
}
Variabelen vTextureCoordinate representerer teksturkoordinat for aktuelt fragment og er en interpolert verdi. Fargeverdien til dette fragmentet hentes («samples») fra teksturen vha. texture()-funksjonen. Denne blandes så med lysfargen og gir endelig farge som tilordnes fragColor.
Noen tips i forhold til Blender og eksport (threejs)
Vha. Blender lager man enkelt en kule (bruk Add | Mesh | UV Sphere i Blender) som eksporteres til et eller annet format, f.eks. obj, fbx e.l. Eksportert fil må deretter importeres i vårt program slik at verteks- og normalvektorbuffer kan fylles med data. NB! Å skrive en slik "importer" selv er en krevende øvelse. Det anbefales derfor å bruke three.js som allerede har slike importere og som derfor gjør denne prosessen relativt enkel. Dette avsnittet er derfor ikke så aktuelt så lenge man kun bruker ren WebGL.
Før eksport fra Blender må man passe på å «triangulere» kula slik at alle «Faces» består av trekanter i stedet for firkanter (trykk Ctrl+T i Edit Mode). I Blender kan man visualisere normalvektorene til kula ved å trykke på "Show Overlays" i edit mode, og velge Normals.
Her kan man velge å se enten flatenormalene, verteksnormalene eller begge deler. I figuren over vises flatenormalene mens figuren under viser verteksnormalene:
Ved eksport fra Blender krysser vi av for «Normals» som vist under:
Kulemodellen kan nå importeres i threejs-programmet slik at verteks- og normalvektorbuffer fylles med data.
Ingen kommentarer:
Legg inn en kommentar