Tomislav Landeka

Uvod

Three.js - JavaScript biblioteka za WebGL-3D u pregledniku

Ova web stranica izrađena je kao seminarski rad na osnovu projketa "Three.js" iz kolegija Računalna grafika na Fakultetu organizacije i informatike. Glavni cilj ovog projekta je dati osnovi uvod u Three.js i prikazati neke konkretne primjere. Pa krenimo...


Three.js je JavaScript library koji omogućuje WebGL - 3D u pregledniku na vrlo jednostavan način. Sadrži velik broj gotovih rutina za iscrtavanje 3D objektata(kocka, valjak, stožac, kugla i sl.). Ti objekti su najčešće oni koje smo i mi sami izrađivali u sklopu kolegija. U izvornom WebGL-u za iscravanje obične kocke potrebno je napisati desetke linija JavaScript koda, a taj isti kod je samo jedan dio Three.js biblioteke. Ti objekti su samo djelić Three.js-a. Pored njih postoje i gotove rutine za postavaljnje kamere(orogonalna ili perspektivna), postavljanje materijala kojim će se iscrtavati objekti, postoji nekoliko vrsta osvjetljenja, učitavači slika(za texturu možemo upotrijebiti jpg, gif, png i slčne formate). Postoji mogućnost spremanja trenutno aktivne matrice za pojedini objekt, pomoćni objekti koji nam pomažu kod izrade 3D scene npr. Axis Helper - dodaje koordinatne osi u scenu.

Korištenje

Uključivanje Three.js biblioteke

Da bi koristili Three.js biblioteku prvo je moramo preuzeti sa stranice Threejs.org sa sljedećeg linka: Threejs.zip. Nakon toga slijedi izrada html dokumenta u koji se mora uključiti Three.js biblioteka na sljedeći način:

        <script src="https://rawgithub.com/mrdoob/three.js/master/build/three.js"></script>
      
Ako koristimo Three.js biblioteku koju smo preuzeli sa prethodno navedenog linka onda moramo uključiti Three.js file koje se nalazi u "build" direktoriju.

Postavljanje scene

Potrebne su nam tri stvari da bi smo mogli bilo što prikazati sa Three.js, a to su: kamera, scena i objekt koji će izraditi(renderirati) scenu sa kamerom, a to je render. Sva tri objekta postavljamo na sljedeći način:
        var scene = new THREE.Scene();
        var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

        var renderer = new THREE.WebGLRenderer();
        renderer.setSize( window.innerWidth, window.innerHeight );
        document.body.appendChild( renderer.domElement );
      
Varijabla "scene" predstavalj scenu koja će se prikazivati pomoću kamere koja je postavljena sa udaljenošću od 75 piksela od točke u koju gleda, i sa veličinom polja koje prikazuje, a koje je veličine od 0.1px do 1000px. Drugi parametar kod dodavanja kamere služi za uključivanje aspect ratio . Na kraju dodajemo render u html dokument. Render koristi <canvas> element za prikaz scene.

Izrada 3D objekta, selektivno odbacicanje, dodavanje na scenu

Da bi smo izradili 3D objekt iskoristit će se jedan od gotovih rutina za definiranje 3D objekta. U ovom slučaju definirati će se kocka. Nakon što pozovemo rutinu za stavanje kocke, potrebno je i odrediti kojim materijalom želimo obojati stranice kocke. Three.js sadrži nekoliko različiti materijala koji mogu primati još dodatne detalje kao što su boja, prozirnost i sl. Sve opisano možemo vidjeti na sljedećem primjeru.
        var geometry = new THREE.CubeGeometry(1,1,1);
        var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
        var cube = new THREE.Mesh( geometry, material );
        cube.position.x += 9;
        cube.material.side = THREE.DoubleSide;
        scene.add( cube );
      
Korištenjem CubeGeometry stvaramo kocku kojoj su duzine stranica 1. Naravno, možemo stvoriti neki objekt kojemu su i različite duljne stranica. Sa MeshBasicMaterial određujemo materijal kojim ćemo obojati kocku. Objekt Mesh uzima predefiniranu kocku i na nju dodaje materijal koji se boja kocka. I na kraju da bi prikazali kocku potrebno je istu dodati na scenu. To je prikazano sa zadnjom linijom koda. U slučaju da kocki želimo promjeniti poziciju to radimo na način da nad objektom cube pozovemo metodu position i još dodamo po kojoj osi želimo vršiti pozicioniranje. Ako želimo gledati prednje poligone onda to dobijemo na način da svojstvu objekta cube.material.side pridružimo vrijednost THREE.DoubleSide . Za prikaz poligona sa stražnje strane koristimo THREE.BackSide .

Skaliranje, translacija, rotacija..

Skaliranje, translacija te rotacija su matematičke transformacije bez kojih ne bi bilo moguću izraditi animaciju. Three.js sadrži gotove funkcije za izvršavanje jedne od ovih matemetičkih transformacija nad objektom. Način korištenja je sljedeći:
        cube.position.x += 5;
        cube.rotation.x = Math.PI;
        cube.scale.x = cube.scale.y = cube.scale.z = 2; 
      
Prva linija koda pomiče kocku po X osi za 5 mijesta. Druga linija koda rotira kocku oko X osi za 180 stupnjva i zadnja linija koda povećava sve stranice kocke za 2.

Osvjetljenje, Helperi, Upravaljnje objektima mišem, Slušači..

Three.js ima gotove rutine za postavaljnje osvijetljenja i sadrži nekoliko vrsta. Ja sam u svojim primjerima koristio PointLight , DirectionalLight i AmbientLight . Jednostavno se definira i postavlja na scenu. U primjeru ispod možemo vidjeti kako se postavlja PointLight . Prvi parametar određuje boju svjetla, a drugi intezitet.
        var light = new THREE.PointLight( 0xffaa00, 3);
        light.position.set(5, 10, 10);
        scene.add( light );
      
Prilikom stvaranj scene, dodavanja objekata, pozicioniranja, rotiranja, mogu nam pomoći Helperi. Ja sam koristio AxisHelper , odnosno, rutinu koja iscrtava koordinatne osi te pomoću njih mogu lakše odrediti u kojem dijelu koordinatnog sustava se nalazi pojedini objekt ili kamera iz koje gledam. Parametar 20 govori kolika je dužina koordinata.
        scene.add(new THREE.AxisHelper(20));
      
Ako želimo upravaljati nekim objektom preko miša tj. ako ga želimo rotirati to možemo postići lako. Prvo moramo uključiti u html datoteku OrbitControls.js. Kod poziva rutine dodajemo objekt kojim želimo upravljati. Ja sam dodao kameru.
        <script src="/examples/js/controls/OrbitControls.js"></script>

        var contrlos = new THREE.OrbitControls(camera);
      
Ako želimo upravaljati nekim objektom preko tipkovnice onda je dovoljno postaviti EventListener i napraviti funkciju koja se poziva pritiskom na tipku. Implementacija se radi na sljedeći način.
        document.addEventListener("keydown", onKeyDown, false);
        /* funkcija koja se poziva pritskom na tipku */
        function onKeyDown( event ) {
        var keyCode = event.which;
        if(keycode == #){}
      }
    

Izrada scene i animacije

Nakon što prethodno navedeno implementramo, opet nećemo moći ništa vidjeti, a razlog je što nismo još ništa renderirali. Da bi to postigli moramo pozvati render loop.
      function animate() {
      /* pozivanje funkcije animate 60 puta u sekundi */
      requestAnimationFrame(animate);
      renderer.render(scene, camera);
    }
    animate();
  
Na kraju pozovemo funkciju animate(); . Isto je potrebno napraviti za svijetlo i kameru ako želimo dobiti rotaciju. Ako želimo dobiti rotaciju kocke odnosno animaciju objekta onda moramo u funkciju animate dodati sljdeći kod i dobit ćemo rotaciju kocke po X i Y osi.
    cube.rotation.x += 0.1;
    cube.rotation.y += 0.1;
  

Problem rotacije

Kad kocku iscratmo u ishodištu i zarotiramo je oko neke proizvoljne osi, za rezultat ćemo dobiti da kocka stoji u mjestu i rotira se. Ako želimo da se kocka rotira oko neke točke npr. pomaknemo kocku za 10 mjesta na osi X i zarotiramo, za rezultat ćemo dobiti da se kocka rotira u mjestu na koje smo je pomaknuli. Ova nije uobičajeno za OpenGL, i ovaj problem se događa zato što objekt kocke koje je stvoren pomoću funkcije THREE.Mesh ne pamti matricu koja se dobije množenjem koordinata kocke i matrice za transformaciju objekta.


Rješenje ovog problema našao sam u THREE.Object3D objektima koji čuva matricu koja se dobije nakon transformacije. Kocku je potrebno pozicionirati, dodati u THREE.Object3D i njega zarotirati. Korištenje možemo vidjeti na sljedećem primjeru:


    var pivot = new THREE.Object3D();
    pivot.add(cube);
    /* dodajemo THREE.Object3D na scenu */
    scene.add(pivot);

    function animate(){
    /* neki kod */
    pivot.rotation.z += 0.1;
  }

Još 3D objekata

Three.js sadrži gotovih dvadesetak rutina za iscrtavanje 3D objekata. Kocka, valjak, stožac, krnji stožac, kugla, polukugla, krožnica, oktaedar itd. Implementacija nekih od njih:
  /* Kocka: kao parametar prima dužine stranica */
  var cube = new THREE.CubeGeometry(1,1,1);
  /* Kvadar */
  var kvadar = new THREE.CubeGeometry(1,5,1);
  /* Kugla sa polumjerom 4 */
  var sphare = new THREE.SphereGeometry(4,16,16, 0, 2 * Math.PI*2, 0, 2 * Math.PI * 2);
  /* Pola kugle */
  var halfSphere = new THREE.SphereGeometry(4,16,16, 0, Math.PI*2, 0, Math.PI / 2);
  /* Valjak - parametri: dužina gornjeg polumjera, dužina donjeg polumjera, visina, broj poligona plašta i baza */
  var roller = new THREE.CylinderGeometry( 3, 3, 8, 20, 50, false );
  /* Stožac */
  var cone = new THREE.CylinderGeometry( 0, 6, 30, 20, 50, false );
}

Praktični dio

Za praktični dio sam izradio pet primjera korištenjem Three.js biblioteke. Primjeri koje sam izradio slični su onima koje smo radili u sklopu vježbi i zadaća na kolegiju Računalna grafika.

Primjer 1. Rotirajući poligoni

Korištenjem miša možemo rotirati kameru.


Primjer 2. Rotirajuća kocka

Korištenjem miša možemo rotirati kocku.


Primjer 3. Rotirajuća kocka sa teksturom.

Zbog nekih problema sa Google Chrome pregldnikom, ovaj primjer je potrebno otvoriti u Firefox-u.


Primjer 4. Vjetromjer

Korištenjem tipki L, K, J, mijenjamo materijal kojim se boja vjetromjer, tipkom I uključujemo samo stražnje poligone, a strelcama Up i Down mijenjamo brzinu vrtnje. Ako brzina vrtnje dosegne određenu brzinu onda dolazi do raspada vjetromjera :). Korištenjem miša možemo rotirati kameru.


Primjer 5. Vjetromjer uz osvjetljavanje

Korištenjem tipki L palimo/gasimo svjetlo, sa F rotiramo svjetlo oko vjetromjera, a strelcama Up i Down mijenjamo brzinu vrtnje. Ako brzina vrtnje dosegne određenu brzinu onda dolazi do raspada vjetromjera :). Korištenjem miša možemo rotirati kameru.



Primjeri:

Primjer 1
Primjer 2
Primjer 3
Primjer 4
Primjer 5

Literatura