Piotr Siekański Wydział Mechatroniki PW Niektóre zaawansowane możliwości biblioteki Three.js Fotorealizm w animacji...2 Cube mapping...2 Skybox...3 Skybox w Three.js...3 Różne materiały dla jednej bryły...4 Sterowanie kamerą za pomocą myszy...6 Ładowanie modeli z Blendera do Three.js...7 Eksport modeli z Blendera do Three.js...7 Funkcje anonimowe w JavaScript...8 Import wyeksportowanych modeli w Three.js...8 Modelowanie powierzchni zwierciadlanych...9 Systemy cząsteczkowe...10 Podstawy systemów cząsteczkowych...10 Liczby losowe w JavaScript...13 Parametry cząsteczek zależne od odległości...14 Zadania...14 1
Fotorealizm w animacji We współczesnej grafice komputerowej duży nacisk kładzie się na uzyskanie tzw. fotorealizmu, czyli stworzenia za pomocą komputera obrazów, które będą dobrze imitować rzeczywiste obiekty. Jeżeli rendering nie musi być przeprowadzany w czasie rzeczywistym, często stosuje się technikę śledzenia promieni (ang. ray tracing), która wymaga bardzo wielu obliczeń i nie nadaje się do generowania animacji w czasie rzeczywistym. Istnieje jednak wiele technik wymagających mniejszej ilości obliczeń, które w konkretnych zastosowaniach dają efekty zbliżone do śledzenia promieni. Cube mapping Cube mapping jest jedną z technik tzw. mapowania środowiskowego (ang. environment mapping). Polega na stworzeniu wirtualnego sześcianu wielokrotnie większego od sceny, którą otacza. Na każdą z jego ścian nakładana jest wcześniej przygotowana tekstura, która odpowiada widokowi z kamery ustawionej prostopadle do ściany (patrz: Rysunek 1). Najczęściej poszczególne ściany w cube mappingu nazywa się zgodnie z następującą regułą: strona osi po której leży dana ściana i nazwa osi. Np. ściana leżąca po dodatniej stronie osi x będzie nazwana px (positive x), a leżąca po ujemnej stronie osi y ny (negative y). Rysunek 1: Cube mapping 2
Skybox Cube mapping jest szeroko stosowany w tworzeniu skyboxów. Skybox jest techniką, która pozwala niskim kosztem uzyskać wrażenie realistycznej sceny o znacznie większych wymiarach niż jest w rzeczywistości. Stosowanie skyboxów stwarza wrażenie dużej odległości obiektów tła, a w rezultacie wrażenie głębi. Skybox w Three.js Dołączony plik Podstawy threejs.html zawiera podstawowy kod HTML konieczny do rozpoczęcia pracy z biblioteką Three.js. oraz zdefiniowane podstawowe zmienne i funkcje. Działanie kodu jest wyjaśnione szczegółowo w pierwszej części instrukcji. Dodano jedynie polecenie lookat, które ustawia kamerę tak, aby patrzyła na obiekt będący argumentem. Jeżeli nie zaznaczono inaczej, wszystkie poniższe polecenia należy dodawać w funkcji init(), żeby zachować czytelność kodu. Skybox jest sześcianem, a więc najpierw należy zdefiniować geometrię sześcianu oraz materiał, z którego będzie wykonany: var skyboxgeometria = new THREE.CubeGeometry( 1000, 1000, 1000); var skyboxmaterial = new THREE.MeshLambertMaterial( { color: 0x00ff00, side: THREE.BackSide ); Warto zwrócić uwagę na parametr side pojawiający się w materiale. Odpowiada on za stronę, po której będzie renderowany materiał. Może przyjmować trzy wartości: THREE.FrontSide THREE.BackSide THREE.DoubleSide Jeśli parametr nie zostanie zdefiniowany, przyjmuje domyślnie wartość THREE.FrontSide, która renderuje materiał po stronie zewnętrznej bryły. W przypadku skyboxa, konieczna jest zmiana wartości tego parametru na THREE.BackSide, ponieważ materiał ma być widoczny wewnątrz sześcianu. Możliwy jest też rendering na obu powierzchniach bryły zewnętrznej i wewnętrznej, wtedy należy ustawić wartość THREE.DoubleSide. Po zdefiniowaniu materiału należy stworzyć bryłę i dodać ją do sceny. var skybox = new THREE.Mesh( skyboxgeometria, skyboxmaterial ); scena.add( skybox ); Materiał skyboxa jest typu lambertowskiego, a więc wymaga zdefiniowania oświetlenia i dodania go do sceny w określonym miejscu: var swiatlopunktowe = new THREE.PointLight( 0xffffff, 2, 1000 ); scena.add(swiatlopunktowe); swiatlopunktowe.position.set(100,100,100); 3
Po zapisaniu i uruchomieniu pliku w przeglądarce, powinno wyświetlić się wnętrze zielonego sześcianu (Rysunek 2). Rysunek 2: Zielony skybox Różne materiały dla jednej bryły Do tej pory każda bryła korzystała z jednego materiału. Skybox musi mieć ich sześć, ponieważ wymaga zastosowania sześciu różnych tekstur. Do tego celu konieczna jest tablica przechowująca każdy z materiałów: var tablicamaterialow = []; Następnie do tablicy muszą zostać dodane materiały, najprostszy sposób to użycie polecenia push, które dodaje jeden lub więcej elementów na koniec tablicy: THREE.MeshBasicMaterial( { color: 0xff0000, side: THREE.MeshBasicMaterial( { color: 0x00ff00, side: THREE.MeshBasicMaterial( { color: 0x0000ff, side: THREE.MeshBasicMaterial( { color: 0xffff00, side: THREE.MeshBasicMaterial( { color: 0xff00ff, side: THREE.MeshBasicMaterial( { color: 0x00ffff, side: Po wykonaniu powyższego fragmentu kodu do tablicy zostanie dodanych sześć materiałów typu basic różniących się kolorem. Kolejnym krokiem jest zmiana typu materiału skyboxa z: var skyboxmaterial = new THREE.MeshLambertMaterial( { color: 0x00ff00, side: THREE.BackSide ); na: var skyboxmaterial = new THREE.MeshFaceMaterial( tablicamaterialow ); 4
Materiał typu MeshFace przyjmuje jako parametr tablicę materiałów, której poszczególne materiały są dodawane do poszczególnych ścian bryły. Materiały w tablicy są typu basic, więc światło punktowe nie będzie już potrzebne i można usunąć fragment kodu odpowiedzialny za dodanie światła: //var swiatlopunktowe = new THREE.PointLight( 0xffffff, 2, 1000 ); //scena.add(swiatlopunktowe); //swiatlopunktowe.position.set(100,100,100); Po zapisaniu pliku i uruchomieniu go w przeglądarce powinien wyświetlić się kolorowy sześcian (Rysunek 3). Rysunek 3: Rózne materiały na każdej ze ścian skyboxa Ostatnim krokiem jest dodanie tekstur do każdej ze ścian. W tym celu należy zmienić fragment kodu odpowiadający za dodawanie materiałów do tablicy z THREE.MeshBasicMaterial( { color: 0xff0000, side: THREE.MeshBasicMaterial( { color: 0x00ff00, side: THREE.MeshBasicMaterial( { color: 0x0000ff, side: THREE.MeshBasicMaterial( { color: 0xffff00, side: THREE.MeshBasicMaterial( { color: 0xff00ff, side: THREE.MeshBasicMaterial( { color: 0x00ffff, side: na 5
THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'tekstury/px.jpg' ),side: THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'tekstury/nx.jpg' ),side: THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'tekstury/py.jpg' ),side: THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'tekstury/ny.jpg' ),side: THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'tekstury/pz.jpg' ),side: THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'tekstury/nz.jpg' ),side: Powyższy fragment kodu ładuje odpowiednie tekstury z dołączonego katalogu nazwanego tekstury i przypisuje je do każdego z materiałów. Nazwa pliku z teksturą odpowiada jej położeniu w skyboxie. Autorem tekstur użytych w przykładzie jest Emil Persson. Rysunek 4: Skybox z teksturami Sterowanie kamerą za pomocą myszy Skybox nie wygląda realnie, gdy obserwator nie znajduje się w jego środku. Należy poprawić pozycję kamery tak, by znajdowała się w środku skyboxa np. poleceniem: kamera.position.set(0,150,0); Tak ustawiona kamera dzięki poleceniu lookat, które jak już wspomniano ustawia kamerę tak, aby patrzyła na obiekt będący argumentem, jest ustawiona zgodnie z kierunkiem wektora kamera centrum sceny - punkt (0,0,0). Dlatego jest skierowana wzdłuż osi y, czyli patrzy na dolną ścianę 6
skyboxa. Kolejnym krokiem będzie implementacja sterowania kamerą za pomocą myszy. W tym celu należy dodać na początku dokumentu dodatek do biblioteki Three.js nazwany OrbitControls znajdujący się w katalogu js. W przypadku ściągnięcia całej biblioteki Three.js znajduje się on w: folder z biblioteką threejs > examples > js > controls > OrbitControls.js <script src="js/orbitcontrols.js"></script> Następnie należy zadeklarować nową zmienną globalną odpowiadającą za sterowanie kamerą: var kontrolakamery; i wewnątrz funkcji init() przypisać ją do kontroli kamery: kontrolakamery = new THREE.OrbitControls( kamera, renderer.domelement ); Parametry odpowiadają kolejno za element, który będzie poruszany za pomocą myszy oraz odwołanie do elementu, HTML wewnątrz którego będzie poruszać się obiekt. W tym przypadku jest nim renderer. Ostatnim krokiem jest dodanie w funkcji animate() polecenia uaktualniającego stan kontroli kamery: kontrolakamery.update(); Po zapisaniu pliku i uruchomieniu go w przeglądarce możliwe będzie sterowanie kamerą za pomocą myszy: lewy przycisk - obrót kamery, prawy przycisk - przesuwanie w płaszczyźnie równoległej, kółko myszy oddalanie/zbliżanie. Ładowanie modeli z Blendera do Three.js Eksport modeli z Blendera do Three.js Blender domyślnie nie wspiera formatu plików używanego przez Three.js, dlatego do biblioteki dołączony jest specjalnie napisany eksporter, który instaluje się jako dodatek do Blendera. Instalacja dodatku przebiega następująco: 1. Skopiowanie dołączonego katalogu io_mesh_threejs do katalogu scripts/addons Blendera. W przypadku ściągnięcia całej biblioteki Three.js znajduje się on w: folder z biblioteką threejs > utils > exporters > Blender. Pełna ścieżka instalacji zależy od używanego systemu operacyjnego: Windows: C:\Users\USERNAME\AppData\Roaming\Blender Foundation\Blender\2.6X\scripts\addons lub C:\Program Files\Blender Foundation\Blender\2.6X\scripts\addons OSX: zależna od katalogu, w którym zainstalowana jest aplikacja; w przypadku instalacji w katalogu Applications: /Applications/Blender/blender.app/Contents/MacOS/2.6X/scripts/addons Linux: /home/username/.config/blender/2.6x/scripts/addons lub /usr/lib/blender/scripts/addons 2. Aktywacja dodatku w Blenderze. W tym celu należy wybrać: File > User Preferences > Addons i w polu wyszukiwania wpisać three. Nastepnie należy zaznaczyć pole wyboru przy Import-Export: Three.js format. 7
3. Model stworzony przy użyciu Blendera można wyeksportować do formatu tekstowego przy pomocy File > Export > Three.js (js). Funkcje anonimowe w JavaScript Funkcja anonimowa rożni się od zwykłej funkcji tym, że nie ma nazwy. Jest deklarowana i natychmiast wywoływana w konkretnym miejscu w kodzie, później nie można się do niej odwołać. Przykład zastosowania funkcji anonimowej do ładowania modelu znajduje się w następnym akapicie. Funkcje anonimowe są często stosowane, ponieważ poprawiają czytelność kodu i przyspieszają jego wykonanie. Są stosowane w działaniach, które wykonują się tylko raz podczas działania skryptu, np. ładowanie modelu z pliku. Import wyeksportowanych modeli w Three.js Biblioteka Three.js posiada wbudowany loader, który umożliwia import modeli zapisanych w tekstowym formacie JSON. Najpierw należy zadeklarować zmienną globalną przechowującą referencję do wczytanego modelu na przykład: var malpa; Loader, przy ładowaniu pliku korzysta z funkcji anonimowej: var zrodlo = "monkey.js"; var loader = new THREE.JSONLoader(); loader.load( zrodlo, function(geometriamodelu){ var malpamaterial = new THREE.MeshBasicMaterial({color: 0x00ff00); malpa = new THREE.Mesh(geometriaModelu, malpamaterial); malpa.position.set(0,0,0); malpa.scale.set(10,10,10); scena.add(malpa); ); Najpierw deklarowana jest zmienna przechowująca ścieżkę do ładowanego pliku, następnie tworzona jest instancja loadera i wywoływana jest jego metoda load(). Przyjmuje ona dwa parametry, pierwszy to ścieżka do modelu, który należy wczytać, a drugi to funkcja anonimowa, która przyjmuje za parametr geometrię modelu wczytaną z pliku. Działanie kodu wewnątrz funkcji anonimowej jest oczywiste: najpierw tworzony jest materiał, później na jego podstawie i geometrii przekazanej przez parametr funkcji anonimowej tworzony jest nowy obiekt, który następnie jest dodawany do sceny. Warto podkreślić, że po wyjściu z funkcji anonimowej wszystkie zmienne użyte w jej wnętrzu staną się niedostępne, więc jeśli nie zostałaby utworzona zmienna globalna przechowująca odwołanie do modelu, model zostałby dodany do sceny i wyświetlany, ale nie można by się do niego w żaden sposób odwołać. Działanie loadera w threejs opiera się o mechanizm AJAX (ang. Asynchronous JavaScript and XML, asynchroniczny JavaScript i XML), który umożliwia asynchroniczne ładowanie poszczególnych elementów strony internetowej. Kod zawarty wewnątrz loadera wykonuje się niezależnie od pozostałej części skryptu, inaczej niż w przypadku klasycznego programowania strukturalnego, w którym kod jest wykonywany linia po linii, a wykonanie kolejnej linii zacznie się dopiero po zakończeniu wykonywania poprzedniej linii. Z tego powodu kod wykonywany asynchronicznie może być znacznie trudniejszy do debugowania. 8
Po zapisaniu pliku i uruchomieniu go w przeglądarce internetowej powinien wyświetlić się zielony model małpy wyeksportowanej z dołączonego pliku monkey.blend i wczytanego z pliku tekstowego JSON monkey.js. Modelowanie powierzchni zwierciadlanych Jak już wspomniano we wstępie do niniejszej instrukcji, raytracing jest zbyt kosztowną techniką, żeby stosować ją w animacjach, które muszą wykonywać się w czasie rzeczywistym. Powierzchnię zwierciadlaną można jednak uzyskać korzystając z wspomnianego już cube mappingu, umieszczając wirtualny sześcian na miejscu obiektu zwierciadlanego i wykonując rendering otoczenia na każdej z jego ścian (patrz Rysunek 1). Tak przygotowaną teksturę należy później nałożyć na obiekt. Biblioteka Three.js posiada gotowy obiekt kamery do cube mappingu. Należy zadeklarować nową zmienną globalną: var kameralustrzana; i w funkcji init() przypisać ją do nowej kamery i dodać ją do sceny: kameralustrzana = new THREE.CubeCamera( 0.1, 5000, 512 ); scena.add( kameralustrzana ); Parametry przyjmowane przez konstruktor to: near obiekty bliższe niż wartośc near nie będą renderowane far obiekty dalsze niż wartośc far nie będą renderowane cuberesolution rozdzielczość kamery; im mniejsza wartość tym mniej dokładne odwzorowanie otoczenia i wrażenie bardziej matowej powierzchni. Następnie należy utworzyć nowy materiał, który będzie zawierał teksturę będącą obrazem z kamery. Umożliwia to parametr envmap: var materiallustrzany = new THREE.MeshBasicMaterial( { envmap: kameralustrzana.rendertarget ); Aby przypisać ten materiał do modelu, należy wrócić do anonimowej funkcji ładującej model i zamienić: var malpamaterial = new THREE.MeshBasicMaterial({color: 0x00ff00); malpa = new THREE.Mesh(geometriaModelu,malpaMaterial); na: malpa = new THREE.Mesh(geometriaModelu,materialLustrzany); Zmienna malpamaterial nie będzie już potrzebna, ponieważ materiał dla obiektu został utworzony poza funkcją anonimową. Jak już wspomniano, w przypadku wykorzystania cube mappingu, konieczne jest aby kamera była ustawiona dokładnie tam, gdzie obiekt, dlatego należy wewnątrz funkcji anonimowej przypisać położeniu kamery realizującej cube mapping położenie obiektu: kameralustrzana.position = malpa.position; 9
Po zapisaniu pliku i uruchomieniu go w przeglądarce załadowany model będzie czarny, ponieważ w każdej klatce animacji należy renderować obraz z kamery realizującej odbicie lustrzane. W tym celu w funkcji render() należy dodać instrukcję uaktualniającą kamerę. Żeby zapobiec sytuacji, w której część obiektu zasłania kamerę powodując powstanie artefaktów na powierzchni obiektu, należy przed uaktualnieniem kamery ukryć obiekt, natomiast po uaktualnieniu, ponownie go pokazać, używając właściwości visible: malpa.visible=false; kameralustrzana.updatecubemap( renderer, scena ); malpa.visible=true; Żeby sprawdzić poprawność działania mapowania środowiskowego można w funkcji animate() dodać obrót modelu: malpa.rotation.x+=0.01; Po zapisaniu pliku i uruchomieniu go w przeglądarce powinien wyświetlić się obracający się model głowy małpy z odbiciem środowiska na powierzchni. Systemy cząsteczkowe Podstawy systemów cząsteczkowych Systemy cząsteczkowe są techniką pozwalającą stosunkowo niewielkim nakładem pracy symulować wiele zjawisk fizycznych, takich jak: ogień, dym, poruszająca się woda, śnieg, deszcz itp. System cząsteczkowy składa się z wielu bardzo prostych elementów, które są animowane niezależnie od siebie. Ze względu na fakt, że aby uzyskać dobry efekt, cząsteczek musi być bardzo dużo, stosuje się wiele metod optymalizacji renderingu. Jedną z nich jest, stosowana w bibliotece Three.js, rezygnacja z geometrii cząsteczki. Są one płaskie i zawsze zwrócone w stronę kamery. W omawianym przykładzie system cząsteczkowy zostanie użyty do tworzenia prostych fajerwerków. System cząsteczkowy tak, jak każdy element w bibliotece Three.js wymaga zdefiniowania geometrii i materiału. Należy zdefiniować dwie nowe zmienne nielokalne fajerwerk, odpowiadającą za wskaźnik do modelu oraz materialfajerwerku, odpowiadającą za przechowywanie materiału: var fajerwerk, materialfajerwerku; Zmienna przechowująca geometrię nie jest potrzebna, ponieważ w omawianym przykładzie, będzie ona tworzona dynamicznie. Następnie w funkcji init(), należy przypisać im wartości: materialfajerwerku = new THREE.ParticleSystemMaterial({ size: 8, map: THREE.ImageUtils.loadTexture( 'tekstury/gwiazdka.png' ), opacity: 1.0, transparent: true, depthtest: false ); fajerwerk = new THREE.ParticleSystem( new THREE.SphereGeometry(30,15,15), materialfajerwerku ); scena.add(fajerwerk); 10
Materiał Fajerwerku jest typu ParticleSystem, co oznacza, że reprezentuje on system cząsteczkowy. Zdefiniowane parametry odpowiadają kolejno za: size rozmiar cząsteczki (domyślnie 1). map tekstura, która będzie wyświetlana. W tym przypadku gwiazdka. Jeśli tekstura nie byłaby zdefiniowana wyświetlany jest kwadrat. Jest to rozwiązanie akceptowalne, jeżeli rozmiar cząsteczki jest mały. opacity stopień przezroczystości cząsteczki w skali 0-1, gdzie 1 oznacza całkowicie nieprzezroczystą. transparent zmienna logiczna, jeśli ma wartość true, umozliwia sterowanie stopniem przezroczystości. depthtest parametr określający, czy cząsteczki mają mieć sprawdzany test głębii. Wartość false przyspiesza rendering i zapobiega powstawaniu artefaktów na krawędzi cząsteczek. Następnie stworzony zostaje system cząsteczkowy na bazie geometrii sfery i wcześniej zdefiniowanego materiału. Cząsteczki pojawiają się w miejsce wierzchołków geometrii. Ostatnim krokiem jest dodanie gotowego systemu do sceny (Rysunek 5). Rysunek 5: Scena z dodanym systemem cząsteczkowym 11
Kolejnym krokiem będzie symulacja efektu wybuchu. Najprostszym wyjściem jest sterowanie skalą systemu, w tym celu utworzone zostaną dwie nowe zmienne globalne skala, odpowiadająca za aktualną skalę systemu oraz maxskala, która będzie zapobiegać rozrostowi fajerwerku w nieskończoność. var skala=1, maxskala=150; Następnie tak przygotowane zmienne należy wykorzystać do sterowania rozmiarem systemu cząsteczkowego. Żeby zwiększyć czytelność kodu, poza funkcą init() zdefiniowane zostaną dwie nowe funkcje resetfajerwerku() i animacjafajerwerku(). function resetfajerwerku(){ skala=1; function animacjafajerwerku(){ skala+=10; fajerwerk.scale.set(skala, skala, skala); if (skala>maxskala) { resetfajerwerku(); Działanie kodu obu funkcji jest oczywiste. Funkcja animacjafajerwerku() zwiększa skalę i na jej podstawie ustawia nową skalę Fajerwerku. Następnie sprawdza czy przekroczona została wartość graniczna, jeśli tak, to wywołana zostaje funkcja resetfajerwerku(), która na obecnym etapie tylko ustawia wartość zmiennej skala z powrotem na 1. Aby rozpocząć animację Fajerwerku należy w funkcji animate() dodać wywołanie funkcji animacjafajerwerku(): function animate() { requestanimationframe( animate ); animacjafajerwerku(); kontrolakamery.update(); render(); Ostatnim krokiem jest zmiana promienia sfery w geometrii Fajerwerku, ponieważ rozrasta się on zdecydowanie za szybko. Należy zamienić: fajerwerk = new THREE.ParticleSystem( new THREE.SphereGeometry(30,15,15), materialfajerwerku ); na: fajerwerk = new THREE.ParticleSystem( new THREE.SphereGeometry(1,15,15), materialfajerwerku ); Po zapisaniu pliku i uruchomieniu go w przeglądarce gwiazdy dookoła małpy powinny pulsować. 12
Liczby losowe w JavaScript Emisję fajerwerku można opisać w bardzo prosty sposób. Wymagane będą do tego dwie nowe zmienne globalne prędkosc odpowiadająca za prędkość lotu fajerwerku i kierunekfajerwerku będąca jego kierunkiem ruchu: var kierunekfajerwerku, szybkosc=10; Następnie, aby tor ruchu każdego z fajerwerków był inny, należy zainicjalizować wektor kierunekfajerwerku trzema losowymi wartościami dzięki poleceniu Math.random(), które zwraca losową liczbę z przedziału (0-1): kierunekfajerwerku = new THREE.Vector3(-1+Math.random()*2, 0.8+Math.random()*0.2, 1+Math.random()*2) Zakres zmienności składowych wektora jest równy odpowiednio dla osi x i z: (-1,1), a dla osi y (0.81). Dzięki tak dobranym parametrom, fajerwerki będą zawsze leciały w kierunku górnej części skyboxa. Następnie należy uaktualnić funkcję animacjafajerwerku() do postaci: function animacjafajerwerku() { if (fajerwerk.position.y <500){ fajerwerk.position.x+=kierunekfajerwerku.x*szybkosc; fajerwerk.position.y+=kierunekfajerwerku.y*szybkosc; fajerwerk.position.z+=kierunekfajerwerku.z*szybkosc; else { skala+=10; fajerwerk.scale.set(skala, skala, skala); if (skala>maxskala) { resetfajerwerku(); Po zapisaniu pliku i uruchomieniu go w przeglądarce internetowej fajerwerk zostanie wystrzelony raz, a później będzie pulsował w górnej części skyboxa. Żeby stworzyć wyrzutnię fajerwerków, należy uaktualnić funkcję resetfajerwerku() do postaci: function resetfajerwerku(){ scena.remove(fajerwerk); fajerwerk = new THREE.ParticleSystem( new THREE.SphereGeometry(1,15,15), materialfajerwerku ); fajerwerk.position.set(0,0,0); scena.add(fajerwerk); skala=1; fajerwerk.scale.set(skala, skala, skala); kierunekfajerwerku = new THREE.Vector3(-1+Math.random()*2, 0.8+Math.random()*0.2, -1+Math.random()*2); 13
Najpierw ze sceny usuwany jest fajerwerk, żeby nie dopuścić do wycieku pamięci przy tworzeniu kolejnych fajerwerków, później na jego miejsce tworzony jest nowy i ustalana jest jego pozycja i skala początkowa, a następnie losowany jest nowy kierunek ruchu. Każdy z fajerwerków może wybuchać w trochę inny sposób. W tym celu można dodać małe składowe losowe do współrzędnych każdej cząsteczki. Do funkcji animacjafajerwerku(), w części odpowiadającej za zachowanie cząsteczek po wybuchu, należy dopisać pętlę iterującą po wszystkich cząsteczkach i dodającą bardzo małe losowe wartości do pozycji każdej z nich: for( var i=0; i<fajerwerk.geometry.vertices.length; i++) { fajerwerk.geometry.vertices[i].x +=(-0.01+Math.random()*0.02); fajerwerk.geometry.vertices[i].y +=(-0.01+Math.random()*0.02); fajerwerk.geometry.vertices[i].z +=(-0.01+Math.random()*0.02); fajerwerk.geometry.verticesneedupdate = true; Działanie powyższej pętli jest proste. W każdej klatce animacji do współrzędnych pozycji każdej cząsteczki dodawane są losowe wartości z przedziału (-0.01-0.01), powodując lekkie zaburzenia geometrii po wybuchu. Aby efekt tych zniekształceń był widoczny należy ustawić zmienną sygnalizującą konieczność uaktualnienia wierzchołków geometrii, co jest realizowane poleceniem: fajerwerk.geometry.verticesneedupdate = true; Parametry cząsteczek zależne od odległości Obecnie cząsteczki po wybuchu oddalają się od centrum wybuchu i po przekroczeniu maksymalnej wartości skali gwałtownie znikają. Dodana zostanie przezroczystość cząsteczek zależna od skali, a przez to od czasu i od odległości od miejsca wybuchu, a także w podobny sposób zmienny kolor. W funkcji animacjafajerwerku() należy dodać aktualizację przezroczystości i koloru na przykład: fajerwerk.material.color.r fajerwerk.material.color.g fajerwerk.material.color.b fajerwerk.material.opacity = = = = 1; 1-(skala/maxSkala); 1; 1-(skala/maxSkala); Dzięki temu w miarę oddalania się cząsteczek od miejsca wybuchu zwiększać się będzie przezroczystość i zmniejszać składowa koloru zielonego, co skutkować będzie fioletowym kolorem wybuchu. Ostatnim krokiem jest aktualizacja funkcji resetfajerwerku(), w której należy dopisać instrukcję odpowiadającą za ustawianie początkowej wartości przezroczystości: fajerwerk.material.opacity = 1; Po zapisaniu pliku i uruchomieniu go w przeglądarce powinien uruchomić się pokaz fioletowych fajerwerków. 14
Zadania 1. Znaleźć w Internecie i wczytać inną teksturę dla skyboxa. 2. Dodać sterowanie połyskiem materiału za pomocą suwaka. Rozdzielczość kamery powinna być zawsze potęgą liczby 2. 3. Wyeksportować dowolny model z Blendera i wczytać go z wykorzystaniem techniki prezentowanej w instrukcji. 4. Zmienić materiał modelu tak, aby zamodelować kolorowe szkło używane do produkcji bombek choinkowych materiał odbijający otoczenie, wrażliwy na światło i posiadający określony kolor (Rysunek 6). 5. Za pomocą systemów cząsteczkowych dodać kilka fajerwerków wystrzeliwanych jeden po drugim. Rysunek 6: Materiał z zadania 4. 15