Nakon osnova koje omogu?avaju po?etak rada, u ovom poglavlju ?e biti opisan postupak za isrtavanje dvodimenzionalnih figura kako bi se lak?e razumeli osnovni delovi WebGL programa. Aplikacija koja se bude postepeno kreirala ?e iscrtavati kvadrat kao primer jednostavne 2D figure.

Cilj je da se u browser-u dobije slede?a slika.

kvadrat u webgl-u

Prvo se postavlja boja pozadine zbog lak?eg uo?avanja granice canvas elementa na html stranici.

<style>
   canvas {background-color: #f5cd2b; }
</style>
                

Naredni zadatak je da se napi?u vertex i fragment shader-e.

//VERTEX SHADER
void main() {
  attribute vec2 vpos;
  gl_Position = vec4(vpos, 0, 1);
}

//FRAGMENT SHADER
void main() {
  gl_FragColor = vec4(0,0,0,1);   }
               

U kodu koji je prikazan prvi deo koda predstavlja vertex shader koji je zadu?en za "postavljanje" objekta odnosno njegovih temena na odgovaraju?e mesto. Vertex shader ?e biti pozvan za svako teme kvadrata i postavi?e koordinate temena tog kvadrata na zadato mesto. Na?in na koji se ovi programi pozivaju kao i opis postupka za postavljanje koordinata temena ?e biti obja?njen u nastavku.

U op?tem slu?aju vertex shader je komplikovaniji i u njemu se izvr?avaju odre?ene transformacije zadatih temena. Shader iz primera je najjednostavniji mogu?i, nema nikakvih transformacija ve? se temena koja su zadata direktno preslikavaju na temena koja ?e biti iscrtana.

Drugi deo koda predstavlja fragment shader koji je zadu?en za boje piksela. Tako?e se radi o najjednostavnijem fragment shader-u koji ?e ?e obojiti svaku ta?ku kvadrata u crno. U op?tem slu?aju boja koja se dodeljuje zavisi od samog vertex-a, odre?enih osobina koje su postavljene u vertex shader-u kao i interpolacije kojom se dobijaju sve ta?ke koje se iscrtavaju na kona?noj slici.

Jo? jednom treba u ovom trenutku podsetiti da ovo nisu JavaScript funkcije ve? programi napisani u shader jeziku (GLSL) koji se izvr?avaju na GPU. Da bi ovi programi mogli biti pozvani moraju se nekako proslediti WebGL-u (gl) kako bi bili kompajlirani i dostupni za pozivanje.

Za postizanje ovoga u webgl kontekstu se kreira jedan program na koji se dodaju (gore navedeni) kompajlirani shader programi. Niz naredbi koji se koristi za ovaj korak je prikazan na slede?em llistingu.

vertexShader = ....
fragmentShader = ....

shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
                

Promenljive vertexShader i fragmentShader predstavljaju kompajlirane shader programe sa po?etka. Pre nego ?to bude navedeno kako se shader programi kompajliraju bi?e navedena trenutna struktura fajla aplikacije.

<!-- Shaders -->
	....Definicija shader programa....
  
<!-- Main script -->
  <script type="text/javascript">
      var canvas;
      var gl;
      var shaderProgram;
	  
      start();
      
      function getShader(...)
      {
          ...return compiled shader
      }
	  
      function initShaders()
      {
          var vertexShader = getShader(...);
          var fragmentShader = getShader(...);

          shaderProgram = gl.CreateProgram();
          gl.attachShader(shaderProgram, vertexShader);
          gl.attachShader(shaderProgram, fragmentShader);
          gl.linkProgram(shaderProgram);
          gl.useProgram(shaderProgram);	 
      }

      function initWebGL(canvas) 
      {
          gl = null;
          try {
              gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
          }
          catch (e) { }
          if (!gl) {
              alert("WebGL nije podr?an.");
          }
      }
	  
      function start()
      {
          var canvas = document.getElementById("canvas_s1");
          initWebGL(canvas);
          initShaders();
      }
 
</script>
                

Sada je potrebno objasniti kako se kompajliraju shader programi. Jedan od na?ina koji se ?esto koristi prilikom u?enja WebGL-a je da se kod za shader programe postavi direktno u kod html stranice unutar script elementa sa jedinstvenom vredno??u id atributa. U momentu kada je shader potreban, iz DOM-a se, koriste?i JavaScript, na osnovu vrednosti id atributa pronalazi sadr?aj script elementa. Taj sadr?aj se preuzima, kompajlira i prosle?uje gl objektu.

Na osnovu gore navedenog mo?e se dopuniti kod aplikacije.

<script id="shader-vertex">
    attribute vec3 vpos;
    void main()
    {
        gl_Position = vec4(vpos, 1);
    }
</script>

<script id="shader-fragment">
    void main()
    {
        gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);   
    }
</script>
                

?esto se script elementima koji sadr?e kod za shader dodeljuje i type atribut koji ukazuje na vrstu shader-a kako bi se koristila dodatna informacija prilikom parsiranja i generisanja shader programa. U tom slu?aju script elementi izgledaju ovako.

<script id="shader-vertex" type="x-shader/x-vertex">
   attribute vec3 vpos;
   void main() 
   {
      gl_Position = vec4(vpos, 1);
   }
</script>

<script id="shader-fragment" type="x-shader/x-fragment">
   void main()
   {
      gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);   }
</script>
                

Navedene vrednosti za type atribut nisu definisane ni po jednoj specifikaciji. Samim tim su ove vrednosti proizvoljne. S obzirom da ?e ovaj deo browser-i ignorisati, koriste?i ove atribute se samo koristi prilika da se obezbedi dodatna informacija.

Dalje je potrebno implementirati JavaScript funkciju koja pronalazi odgovaraju?e script elemente, parsira ih i kompajlira njihov sadr?aj.

function getShader(gl, id) {
   var shaderScript, shaderSource, cur, shader;

   shaderScript = document.getElementById(id);
   if (!shaderScript) {
      return null;
   }
		
   shaderSource = "";
   cur = shaderScript.firstChild;
   while(cur) {
      if (cur.nodeType == 3) {
         shaderSource += cur.textContent;
      }
      cur = cur.nextSibling;
   }
	
   if (shaderScript.type == "x-shader/x-fragment") {
      shader = gl.createShader(gl.FRAGMENT_SHADER);
   } else if (shaderScript.type == "x-shader/x-vertex") {
      shader = gl.createShader(gl.VERTEX_SHADER);
   } else {
      alert('greska');
      return null;
   }
	
   gl.shaderSource(shader, shaderSource);
   gl.compileShader(shader);
	
   if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {  
    alert("Gre?ka prilikom kompajliranja shader programa: " + gl.getShaderInfoLog(shader));  
    return null;  
   }

   return shader;
}
                

Navedeni kod bi trebalo da je prili?no jasan. Pronalazi se script sa zadatom vredno??u id atributa, formira se izvorni kod shader programa (string) na osnovu sadr?aja, formira se odgovaraju?a vrsta shader -a na osnovu zadatog tipa script elementa i na kraju se shader kompajlira.

Sada su shader programi postavljeni i dalje treba da se poka?e kako oni dobijaju podatke o temenima kvadrata.

Potrebno je prvo kreirati strukturu u koju se sme?taju vrednosti koje predstavljaju temena kvadrata.

function initBuffers() {
  var vertices = [
	-0.5, -0.5, 0.0,
	0.5,  -0.5, 0.0,
	0.5,   0.5, 0.0,
	-0.5,  0.5, 0.0  ];
  vBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
}
                

Izbor vrednosti za koordinate temena kvadrata mo?e u ovom momentu izgledati ?udno. Izbor je ovakav zbog prirode koordinatnog sistema. O kooridnatnom sistemu koji se koristi u radu sa WebGL-om ?e kasnije biti re?i, za sada je dovoljno znati da WebGL poznaje koordinate samo u rasponu od -1 do 1. S obzirom da se u ovom primeru iscrtava 2D objekat vrednost poslednje komponente svih koordinata je 0.

Mo?e se primetit da je vertices[] jednodimenzionalni niz, a njegov zapis u editoru je ovakav kako bio lak?ะต (programeru) da se uo?e vrednosti koordinata temena.

Na osnovu podataka u nizu se kreira jedan bafer (vBuffer). Baferi su strukture (nizovi u GPU memoriji) kojima WebGL upravlja kada do?e do iscrtavanja. Oni predstavljaju mehanizam koji se koristi za alociranje, inicijaizaciju i iscrtavanje iz GPU memorije.

U bafere se sme?taju podaci o temenima i bojama kao i ostalim osobinama objekata koji se iscrtavaju. Na osnovu instrukcija programera WebGL zna kako da interpretira i iskoristi vrednosti iz ovih bafera.

Prilikom popunjavanjabafera podacima (bufferData) postavlja se vrednost koja ukazuje na na?in kori??enja datog bafera. U ovom radu se kroz primere koristi vrednost STATIC_DRAW koja ozna?ava da ?e podaci koji su u baferu biti samo jednom kreirani, ali se mogu koristiti vi?e puta prilikom iscrtavanja. Osim ove, mogu se koristiti i DYNAMIC_DRAW i STREAM_DRAW vrednosti. Bez obzira ?ta je izabrano, baferi se mogu proizvoljno koristiti, ali postavljanje ove vrednosti na onu koja najvi?e odgovara potrebama aplikacije mo?e uticati na performanse aplikacije.

U bafferu se sada nalaze vrednosti za temena kvadrata i dalje ih je potrebno proslediti vertex shader-u koji je eksterni kompajlirani program. Ponovo, vertex shader je definisan na slede?i na?in.

attribute vec3 vpos;
void main() {
   gl_Position = vec4(vpos, 1);
}
                

Vrednosti iz aplikacije se shader-ima prenose preko promenljivih deklarisanih sa attribute. Ove promenljive se kratko nazivaju atributi. Shader iz primera ima jedan atribut vpos koji je definisan kao vec3, tip koji se koristi za predstavljanje trodimenzionalnih vektora (podsetimo se da su temena kvadrata zadata sa po 3 vrednosti).

Promenljiva gl_Postition je jedna od standardnih (built-in) promenljivih koja sadr?i poziciju trenutnog temena u obliku 4-dimenzionalne homogene kooridnate. Krajnji zadatak svakog vertex shader-a je upravo da postavi vrednost ove promenljive.

Shader iz ovog primera postavlja kona?nu poziciju temena kvadrata ba? na zadatu vrednost (nema nikakvog izra?unavanja ili transformisanja). To zna?i da ?e u prvom pozivu postaviti prvi vertex na poziciju (-0.5, -0.5, 0.0), drugi na (0.5, -0.5, 0.0) i tako dalje.

Poslednja vrednost u defuiniciji koordinate predstavlja ?etvrtu komponentu homogene koordinate i po?to je vpos dimenzije 3, ?etvrta koordinata se za sada postavlja na 1.

Dalje treba obezbediti mehanizam kojim ?e se iz spoljne aplikacije vrednosti iz niza koji sadr?i podatke o temenima te vrednosti proslediti atributu vpos (koji je u shader memoriji).

Prenos podataka ovoj promenljivoj se radi na slede?i na?in.

var positionLocation = gl.getAttribLocation(shaderProgram, "vpos");
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);
                

Funkcijom gl.getAttribLocation se dobija referenca u memoriji na zadati atribut. Dalje je potrebno sukcesivno postavljati koordinate temena kvadrata na to mesto. Iz tog razloga se koristi enableVertexAttribArray koji omogu?ava primenu operacija nad nizovima, a naredbom vertexAttribPointer se interpretira taj dolaze?i niz vrednosti.

Drugi parametar u pozivu funkcije vertexAttribPointer je broj koji ozna?ava koliko komponenti niza zauzima vrednost jednog atributa. Na primer, ovaj niz i odgovaraju?i atribut su mogli predstavljati 2D koordinate ili niz rgba vrednosti u kom slu?aju bi parametar imao vrednost 2 odnosno 4. Atribut iz ovog primera je definisan kao vpos3 i dobija?e sukcesivno koordinate temena kvadrata koje su zadate sa po 3 vrednosti.

Na ovaj na?in je povezan niz sa temenima i shader promenljiva koja postavlja kona?ne pozicije vertex-a.

U nastavku ?e biti poja?njen fragment shader.

Kao ?to vertex shader koristi gl_position da postavi kona?nu poziciju trenutnog temena, fragment shader koristi built-in promenljivu gl_FragColor da postavi boju za svaki piksel koji se iscrtava.

Shader je definisan na slede?i na?in:

<script id="shader-fragment" type="x-shader/x-fragment">
   void main() {
      gl_FragColor = vec4(0, 0, 0, 1.0);   }
</script>
                

Ovaj shader je jo? jednostavniji od vertex shader-a. S obzirom da se svaki piksel boji istom bojom nema potrebe za prenosom nikakvih vrednosti iz aplikacije (nema atributa). Gore navedena vrednost (0, 0, 0, 1.0) predstavlja crnu boju. Vi?e o bojama u WebGL ?e biti re?i u nastavku.

Re?eno je da se fragment shader poziva za svaki piksel. Koji pikseli ?e se na kraju biti iscrtani delom zavisi od poslednje funkcije koju pozivamo, a to je drawArrays().

gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
                

glDrawArrays je funkcija koja na kraju daje instrukciju WebGL-u kako da izvr?i iscrtavanje. Ona defini?e koje, takozvane, primitive treba iscrtati koriste?i podatke iz zadatog niza.

U primeru se koristi primitiva TRIANGLE_FAN koja iscrtava trouglove i ona predstavlja jednu od nekoliko koje WebGL podr?ava. O vrstama primitiva ?e biti re?i kasnije.

Drugi i tre?i parametar u pozivu funkcije ozna?avaju indeks po?etnog elementa u nizu i ukupan broj elementa niza koji ulaze u iscrtavanje krajnjeg rezultata.

Na osnovu opisa ovog primera se dobija jasnija slika o procesima koji se de?avaju prilikom iscrtavanja u WebGL-u.

Kada se pozove funkcija za iscrtavanje poziva se vertex shader koji izra?unava kona?ne pozicije vertex-a. U vertex shader-u se, u op?tem slu?aju, pozicije vertex-a na neki na?in tranformi?u, transliraju se, rotiraju i sli?no, zavisno od ciljeva aplikacije. Kada vertex shader zavr?i sa radom, WebGL odre?uje interpolacijom i ostale piksele koje je potrebno iscrtati na ekranu i za svaki od njih se poziva fragment shader. U fragment shader-u se u op?tem slu?aju postavljaju razli?ite boje zavisno od svetla i tekstura koje se koriste u aplikaciji. Tokom rada se formiraju kona?ni baferi sa podacima koji se prikazuju na ekranu.

Na po?etku rad u shader jeziku kao i koncepti koji se koriste u radu sa WebGL-om mogu delovati komplikovano, ali vrlo brzo stvari postaju jasnije.

Videli smo da se postavljanje boje u fragment shader-u se radi na slede?i na?in.

gl_FragColor = vec4(0, 0, 0, 1.0);
                

Kao ?to je re?eno, ovim se boja piksela postavlja na crnu.

Boje se u WebGL-u zadaju kao takozvane RGB ili RGBA boje, gde vrednost svake komponente odre?uje jedan kanal: crvena (R), zelena (G), plava (B) i alfa/transparencija (A).

Sve vrednosti se zadaju u opsegu [0,1] ?to mo?e biti druga?ije od onoga kako je ?italac navikao ako ima iskustva u radu sa html i css tehnologijama.

Jednostavnom pormenom u fragment shader-u se dobija kvadrat druga?ije boje.

gl_FragColor = vec4(0, 0, 1.0, 1.0);
                
calibre de diver automatic men's watch rolex datejust ii gold replica watches uk tag heuer tom brady watch price buy replica watches toronto replica watches uk replica watches shop in gaffar market audemars piguet royal oak offshore carbon rolex replica breitling watch dating movado canada head office replica watches uk is breitling a luxury brand patek philippe pocket watch antique roadshow uk replica watches

Druga stvar koja se mo?e promeniti je niz koji predstavlja temena kvadrata. U stvari, u primeru je iscrtan kvadrat samo zato ?to su koordinate postavljene tako, a ne zato ?to je negde pozvana funkcija koja iscrtava kvadrat. Ako se promene vrednosti koordinata u nizu dobijaju se razli?ite figure.

var vertices = [
   -0.5, -0.5, 0.0,
   0.5, -0.5, 0.0,
   0.5, 0.8, 0.0,
   -0.5, 0.5, 0.0
];
                

Ranije je re?eno da od programera zavisi kako ?e se interpretirati niz koji sadr?i vrednosti za vertex shader. Ako se izvr?e promene kao na slede?em listingu dobi?e se isti rezultat.

//shader
....
attribute vec2 vpos;
void main() {
   gl_Position = vec4(vpos, 0, 1);
}

...		  
var vertices = [
   -0.5, -0.5,  
    0.5, -0.5,  
    0.5,  0.5, 
   -0.5,  0.5
];

...
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
                

Pozivaju?i funkcju drawArrays parametar koji odre?uje vrstu primitiva kojom se vr?i iscrtavanje u primeru je postavljen na TRIANGLE_FAN, a to je samo jedan od mogu?ih nekoliko koje WebGL podr?ava. Promenom izbora vrste primitiva dobijaju se druga?ije vrednosti. U nastavku, u poglavlju 5, ?e biti navedene sve vrste primitiva.

Napredak

% pre?eno

Slede?e - Primitivi u WebGL-u

Slede?a lekcija