Svetlo, samo po sebi je komplikovan pojam i u stvarnom svetu. Postoje razni aspekti koji uti?u na to kako i koliko ?e ne?to biti osvetljeno: raspored izvora svetla i njihovih osobina, refleksije, transparencije objekata i sli?no. Zato je i simulacija osvetljenja u 3D grafici kompleksna tema i detalji vezani za teoriju koja omogu?ava modeliranje osvetljenja kao i prakti?ni na?ini implementacije su van okvira ovih lekcija. U nastavku ?e biti opisan samo primer najjednostavnijeg osvetljenja i osnovni koncepti u radu sa svetlom u WebGL-u.
Jedan od najpoznatnijih modela osvetljenja je Phong Lighting Model po kom je rezultuju?e osvetljenje u ta?ki kombinacija nekoliko vrsta osvetljenja. Naj?e??e se u modeliranju osvetljenja govori o slede?im vrstama svetla:
U primeru koji sledi bi?e prikazana kocka sa ambijentalnim i jednim izvorom direktnog osvetljenja (bez point i emissive osvetljenja). Tako?e, koriste?e se ?injenica da kocka ima ravne povr?ine i da je dovoljno izra?unati osvetljenje na vertex-ima dok ?e se ostale vrednosti dobiti interpolacijom.
Za modeliranje ovakvog osvetljenja i implementaciju shader-a potrebne su dve stvari:
U slu?aju direktnog osvetljenja ugao kojim svetlost sti?e do vertexa-a na povr?ini objekta je uvek isti. Me?utim, efekat osvetljenja vertex-a zavisi od ugla izme?u zraka i povr?ine na kojoj le?i vertex, odnosno izme?u vektora osvetljenja i odgovaraju?eg vektora normale. Uz pomo?e geometrije i linerane algebre se mo?e pokazati da je ova vrednost jednaka skalarnom proizvodu ova dva vektora. Treba napomenuti da ukoliko je ugao izme?u vektora normale i vektora osvetljenja ve?i od 90 (povr?ine koje su "iza", koje nisu osvetljene) dobijaju se negativne verednosti za skalarni proizvod tako da ?e se prilikom izra?unavanja uzimati ve?a vrednost u izboru izme?u 0 i vrednosti koja se dobija skalarnim proizvodom.
?to se ti?e ambijentalnog osvetljenja ono se ne menja sa promenom vertex-a tako da ?e vrednosti koje odgovaraju ovom osvetljenju biti nepromenljive, ali ?e u?estvovati u zra?unavanju kona?ne vrednosti osvetljenja.
Rezultat primera koji implementira ovakvo osvetljenje je dat na slede?oj slici.
Na slici se vidi da ?e plava i zelena komponenta direktnog osvetljenja biti poja?ane u odnosu na crvenu kako bi efekat osvetljenja bio uo?ljiviji. Iz istog razloga je promenjena i boja pozadine u ovom primeru.
Prvo se vr?e izmene na shader-ima. Fragment shader je je jednostavan i intuitivan.
<script id="shader-fragment" type="x-shader/x-fragment"> varying highp vec2 vTexture; varying highp vec2 vLighting; uniform sampler2D uSampler; void main() { highp vec4 textureColor = texture2D(uSampler, vTexture); gl_FragColor = vec4(textureColor.rgb*vLighting, textureColor.a) } </script>
Osobine osvetljenja se od vertex shader-a prenose preko varyng promenljive kako bi se ova pode?avanja primenila na boju za dobijanje kona?nog rezultata.
Vertex shader je komplikovaniji u tom smisli da se mora implementirati i dodavanje vrednosti osvetljenja za svaki vertex.
<script id="shader-vertex" type="x-shader/x-vertex"> attribute vec2 aVertexTexture; attribute vec3 aVertexPosition; attribute vec3 aVertexNormal; uniform mat4 uMvMatrix; uniform mat4 uPMatrix; uniform mat3 uNormalMatrix; varying vec2 vTexture; varying vec3 vLighting; highp vec3 ambientLightColor = vec3(0.6, 0.6, 0.6); highp vec3 directionalLightColor = vec3(0.0, 0.8, 0.7); highp vec3 directionalVector = vec3(0.5, 0.5, 0.5); highp vec3 transformedNormal = uNormalMatrix * aVertexNormal; highp float directional = max(dot(transformedNormal, directionalVector), 0.0); void main() { gl_Position = uPMatrix * uMvMatrix * vec4(aVertexPosition, 1); vTexture = aVertexTexture; vLighting = ambientLightColor + (directionalLightColor * directional); } </script>
Svi vertex-i ?e imati svoj vektor normale i zato se defini?e atribut aVertexNormal
.
Uniform promenljiva uNormalMatrix
predstavlja matricu kojom se transformi?u ovi vektori normala ?ime se dobijaju vrednosti za transformedNormal
.
U nastavku ?e biti obja?njeno kako se formira ova matrica.
RGB vrednosti osvetljenja se postavljaju kao konstantne sa ambientLightColor
i directionalLightColor
,
a vektor pravca osvetljenja se postavlja u directionalVector
promenljivu.
Za izra?unavanje intenziteta svetla koristi se skalarni proizvod, a on se dobija pozivom funkcije dot
koja je dostupna u GLSL.
Na kraju se, kao kona?na vrednost osvetljenja, dobija vrednost za vLighting
(koja se prenosi u fragment shader)
kao zbir ambijentalnog osvetljenja (isti intenzitete svuda) i proizvoda direktnog osvetljenja i njegovog intenziteta za zadati vertex.
Bafer koji sadr?i vrednosti za vektore normala je prikazan na slede?em listingu.
vertexNormals = [ 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0 ]
S obzirom da se u primeru koristi kocka, vrednosti za vektore normala je lako intuitivno definisati. Po?to objekat (kocka) nije vi?e u model koordinatama ve? su izvr?ene neke transformacije potrebno je i odgovaraju?e vektore normala transformisati. Problem je ?to se ne mo?e iskoristiti model-view matrica za transformaciju vektora normala. To je zato ?to ove vrednosti predstavljaju vektore, a ne ta?ke u prostoru za razliku od vertex-a.
Na primer, ukoliko se pretpostavi da postoji samo translacija i to po jednoj osi samo, radi lak?eg razumevanja, model-view matrica bi bila matrica translacije. Ako bi se ova matrica primenila na vektor normale vrednost te jedne komponente (po kojoj se vr?i translacija) bi se promenile. To onda vi?e ne bi bio isti vektor normale i samim tim rezultat ne bi bio o?ekivan. S druge strane, na primer, rotacija ne bi prouzrokovala dati problem. Zato u ovakvim jednostavnim primerima (samo sa translacijom) postoji na?in da se iskoristi model-view matrica (?etvrta koordinata se postavi na nulu umesto na 1 kako bi se anulirala translacija). Me?utim, ovo nije univerzalno re?enje.
S obzirom da se svetlo intenzivno koristi u 3D grafici, iako mogu?e, u ovom primeru se ne koristi pre?ica ve? se defini?e op?tiji postupak.
U biblioteci glMatrix postoji funkcija mat3.normalFromMat4(out, a)
koja upravo kreira odgovaraju?u matricu za transformaciju normala
na osnovu zadate model-view matrice. Ova funkcija interno implementira transponovanje inverzne polazne matrice (njenog gornjeg levog 3x3 dela)
?to je u skladu sa matemati?kom teorijom koja dokazuje da je ovo ispravno re?enje za transformaciju normala.
Ova funkcija se koristi za inicijalizaciju materice transformacije normala.
function initMatrices() { pMatrix = mat4.create(); mat4.perspective(pMatrix, Math.PI / 4, 400 / 400, 1, 10000.0); mvMatrix = mat4.create(); mat4.translate(mvMatrix, mvMatrix, [0, 0, -10.0]); mat4.rotateY(mvMatrix, mvMatrix, Math.PI / 6); mat4.rotateX(mvMatrix, mvMatrix, Math.PI / 6); normalMatrix = mat3.create(); mat3.normalFromMat4(normalMatrix, mvMatrix); }
Na kraju ostaje da se popune baferi, referenciraju attribute
i uniform
promenljive i da se pozove iscrtavanje.
Po?to su ove funkcije identi?ne onima koje su kori??ene do sada ovde ne?e biti navedene.
OpenGL ima ugra?ene funkcionalnosti koje omogu?avaju kreiranje izvora svetla i manipulaciju njima. WebGL nema.