3D Fraktali

Projekt izradili: Gabrijel Bartošek & Samuel Picek

Ovdje možete preuzeti: izvorni kod ovog projekta - seminar u PDF formatu - prezentacija u PDF formatu

1.Uvod

Jedno od zanimljivijih područja računalne grafike zauzimaju matematičke umotvorine, tj. fraktali. Cijeli svijet oko nas, te unutar nas zapravo je i sačinjen od nekakvoga vida fraktala, a mogli bi slobodno reći da je i metodika našeg razmišljanja fraktalna.

U davnoj prošlosti matematika je bila pretežito orijentirana na skupove i funkcije nad kojima su se pretežito primjenjivale metode klasičnog diferencijalnog računa. Skupovi funkcija koji nisu bili dovoljno glatki ili regularni, pretežito su se ignorirali. U posljednjim desetljećima ovakav se način razmišljanja uvelike promijenio.

Fraktali su čudesni geometrijski oblici koji se sastoje od umanjenih verzija samih sebe (svaki dio je umanjena kopija cjeline). Nalazimo ih posvuda oko nas, te ih vidimo svaku dan, mada ih ne primjemjećujemo. Možemo ih zapaziti na običnoj brokuli, cvjetači, ali isto tako u planinskim lancima, te deltama rijeka. No međutim osim onih u prirodi, mi ćemo se u seminarskom radu fokusirati na one fraktale koje stvara čovjek, kako bi se umjetnički i vizualno posebno izrazio.

Prvenstveno fraktalna umjetnost je ona umjetnost koja nastaje uz pomoć računala i matematike. Fraktalna geometrija nam daje generalni okvir gdje se proučavaju iregularni skupovi. Premda dosta često u današnjici čujemo riječ fraktali, velika većina neće razumjeti što oni točno predstavljaju i što znače. Bilo je mnogo pokušaja da se fraktali definiraju u čisto matematičkom smislu, no te definicije često su se ispostavile neispravnima u općem smislu značenja. Međutim, fraktalna geometrija daje podosta tehnika za upravljanje fraktalima.



2.Povijest fraktala




Kao što smo u uvodu naveli, fraktali koje stvara čovjek nastaju pomoću računala i matematike. Računala danas pronalaze mnogobrojne primjene povezujući grafiku i matematiku u pogledu umjetnosti, npr. digitalno obrađene fotografije i dizajn. S prvom pojavom računala, umjetnici su se zapitkivali na koji način bi mogli iskoristiti taj novitet u svom području, tj. za izradu svojih radova.

Računala koja su prva našla svoju primjenu u aspektu našeg privatnog, ali i poslovnog života, zasigurno su našla svoju primjenu i u umjetnosti, tako da danas već koristimo izraz za to „digitalna umjetnost“. Takva umjetnost je izazvana uz pomoć računala, a na to možemo gledati kao sredstvo, odn. materijal i tehniku u isto vrijeme baš kao što su slikaru kist, boje i platno. Poput fraktalnog umjetnika, kipara ili bilo koga drugog umjetnika, potrebna su pomoćna sredstva da bi stvorili umjetničko djelo, no bez vlastite kreativnosti i rada to ne bi moglo proizlaziti.

Fraktali su ljudima poznati od pamtivijeka, samo što se oni na taj način nisu prepoznavali. Prvi dokumentirani prikaz fraktala možemo naći već 1525. god. u „Priručniku za slikanje“ Albrechta Dürera gdje se opisuju uzorci nastali korištenjem pentagona. U 17. st. Leibnitz je definirao ponavljanje samosličnosti, no međutim uzeo je u obzir da samo linija može biti sebi slična. Od tada, pa do 19.st. nisu se javljale nikakve slične definicije. Tek 1872. god. Karl Weierstrass daje primjer funkcije kojom je definirao samosličnost. Takva definicija je bila suviše apstraktna, pa je Helge von Koch 1904. god. dao geometrijsku interpretaciju slične funkcije, koja je danas poznata kao Kochova pahulja, a to će nam ujedno biti i projektni zadatak iz ovog kolegija. Poslije toga 1915. god. Waclaw Sierpinski kreirao je svoj uzorak fraktala pomoću trokuta. U tome razdoblju pronalazimo dosta fraktalnih prikaza poput onih Pierrea Fatoua, Henria Poincaréa, Georgea Cantora, Gastona Julie i Felixa Kleina. Svi oni su djelovali krajem 19. st. i početkom 20. st. i proučavali su te fascinantne tvorevine dobivane iteracijama, no međutim bez računala, pa nisu mogli uočiti sav njihov značaj.

Prvi puta se termin „fraktal“ pojavljuje 1975. god. kojeg je upotrijebio matematičar Benoit Mandelbrot, a fraktalna umjetnost se počinje razvijati tek sredinom 1980-ih godina. Prva fraktalna slika je nastala na naslovnoj stranici časopisa „Scientific American“ 1985. god. i prikazivala je Mandelbrotov skup.

Slika 1 - Scientific American - Mandelbrotov skup [Shane Bow The Chaos of Mandelbrot, dostupno na: http://shanebow.com/projects/mandelbrot/, učitano: 17.01.2015.]

Takva slika je nastala s intencijom da ponudi vizualne i umjetničke kvalitete. Nedugo nakon toga izdana je bogato ilustrirana knjiga „The Beauty of Fractals“ čiji su autori Heinz-Otto Peitgen i Peter Richter. U toj knjizi velik broj ilustracija se temelji na drugom najpopularnijem fraktalu, a riječ je Juliaovu skupu. [Fraktali i umjetnost, Vesna Mišljenović, Zagreb (2011./2012. br. 80), str. 223.]

Kako smo konstatirali da je fraktalna umjetnost vrlo mlada, ona nije još pronašla svoje pravo mjesto u „mainstream“ umjetničkim krugovima, a isto tako nije zastupljena na tržištu umjetnosti i galerijama. Međutim možemo reći da fraktalna umjetnost ima široku primjenu u računalnoj animaciji, konkretno u simulaciji rasta biljaka ili primjerice generiranju krajolika.




3.Definicija fraktala



Fraktali su slike nastale uzastopnim ponavljanjem neke matematičke funkcije, odnosno ponavljanjem određenog geometrijskog postupka. Mogli bismo reći da su to zapravo objekti koji daju jednaku razinu detalja neovisno o razlučivosti koju koristimo.

Kako smo na laboratorijskim vježbama mogli vidjeti kod profesora Horvata, fraktale je moguće uvećavati beskonačno mnogo puta, a pri tome se prilikom svakog novog uvećanja mogu opaziti detalji koji prije povećanja nisu bili vidljivi, a da količina novih, možemo reći i sitnijih detalja uvijek bude otprilike jednaka.

U uvodu je važno za napomenuti da fraktali imaju svoja osnovna svojstva, a to su: [Uvod u matematičke metode u inženjerstvu, Fraktali, dostupno na: http://matematika.fkit.hr/novo/izborni/referati/dobrinic_joskic_brdar_fraktali.pdf, 3. str. učitano: 17.01.2015.]

  1. Samo-sličnost – svojstvo objekta da sliči sam na sebe, neovisno o tome koji dio promatramo i koliko ga puta uvećavamo.

  2. Fraktalna dimenzija – vrijednost koja nam daje uvid u to kojoj mjeri pojedini fraktal ispunjava prostor u kojem se nalazi. Za razliku od fraktalne dimenzije, euklidska dimenzija koristi se kako bi se izrazila linija (jedna dimenzija), površina (dvije dimenzije) i prostor (tri dimenzije), te može biti bilo koji prirodni broj ili nula (0, 1, 2, 3, 5, 10, 100, ... ). Fraktalna dimenzija, nasuprot tome koristi se kako bi se postigla gustoća kojom objekt ispunjava prostor, tj. koliko se novih dijelova pojavljuje pri uvećavanju rezolucije. Fraktalna dimenzija nije cijeli broj i u pravilu je veća od euklidke dimenzije.

Primjer:

Fraktalna dimenzija zapadne obale Engleske je 1,3 dok je od Norveške obale 1,52. One su veće od 1, na temelju čega možemo zaključiti da su euklidske obale shvaćene kao krivulje. Isto tako to bi značilo da Norveška ima razvodnjeniju obalu od Engleske. Samo bi približno mogli na eksperimentalnoj razini govoriti o fraktalnim dimenzijama obale.
Izraz po kojem se mjeri dimenzija je:

d=log(n)/log(s),

a pri tome je:


Primjer:

Cantorov skup C3 ima dim C3 = log2/log3 = 0.63..... < 1;

Kochova krivulja KK ima dim KK = log4/log3 = 1.2618..... > 1.


  1. Oblikovanje iteracijom – svojstvo da se objekt generira nekim matematičkim ili geometrijskim postupkom, tako da se u osnovni (početni) objekt iterativno ugrađuju svojstva generatora.



3.1.Podjela fraktala


Fraktale dijelimo prema:

  1. stupnju samosličnosti

  2. načinu nastanka.


3.1.1.Podjela fraktala prema stupnju samosličnosti


Kod podjele fraktala prema stupnju samosličnosti možemo razlikovati:

  1. potpuno samoslične fraktale

  2. kvazi samoslične fraktale

  3. statičke samoslične fraktale


Potpuno samoslični fraktali sadrže kopije sebe koje su slične cijelom fraktalu. Ovdje je vrlo bitno poznavati bazu i motiv. Baza je bilo koji oblik koji je sastavljen od linijskih segmenata, dok je motiv neki drugi oblik koji se također sastoji od linija. Ako se svaka linija baze nadomjesti oblikom motiva i taj proces nastavi u beskonačnost, dobivamo fraktal.





Slika 2 - Baze i motivi [Uvod u fraktale, M.Paušić, dostupno na: http://www.fer.unizg.hr/_download/repository/Uvod%20U%20Fraktale%20by%20Mladen%20Pausic.pdf, učitano: 17.01.2015.]

Potpuno samoslični fraktali su svi geometrijski fraktali, a primjere za to možemo vidjeti na slikama ispod:



Slika 3 - Kochova krivulja [Hrvatski matematički elektronski časopis math.e, Galerija fraktala, V. Antočić, A. Galinović, dostupno na: http://e.math.hr/galerija/galerija_print.html, učitano: 17.01.2015.]

Kochovu krivulju uveo je švedski matematičar Helge von Koch. Fraktalna dimenzija Kochove krivulje je log4 / log3 = 1,2619.


Slika 4 - Nastanak Kochove pahuljice (2D) [Hrvatski matematički elektronski časopis math.e, Galerija fraktala, V. Antočić, A. Galinović, dostupno na: http://e.math.hr/galerija/galerija_print.html, učitano: 17.01.2015.]

Napomena: razlika između Kochove krivulje i Kochove pahuljice je u tome što krivulja počinje dužinom, a pahuljica jednakostraničnim trokutom.

Kochovu pahuljicu ćemo kasnije prikazati u praktičnom primjeru: OpenGL ES 2.0 & 3.0 na Androidu - 3D fraktali (primjer Kochove pahuljice).


Slika 5 - Sierpinskijev trokut - kontrukcija otkidanjem trokuta [Hrvatski matematički elektronski časopis math.e, Galerija fraktala, V. Antočić, A. Galinović, dostupno na: http://e.math.hr/galerija/galerija_print.html, učitano: 17.01.2015.]

Sierpinskijev trokut uveo je poljski matematičar Waclaw Sierpinski. Fraktalna dimenzija Sierpinskijevog trokuta iznosi log3 / log4 = 1,584962.

Poslije većeg broja iteracija možemo opaziti da duljina Kochove krivulje teži u beskonačnost kada i broj iteracija teži u beskonačnost. Ali cijela ta duljina je i dalje na istoj površini, samo možemo reći da je malo više zgužvana. Stupanj te zgužvanosti možemo vidjeti iz fraktalne dimenzije. To nam daje uvid u to u kojoj mjeri nekakav fraktal zauzima ravninu ili općenito n-dimenzionalni prostor u kojem se nalazi. Tako primjerice Kochova krivulja ima fraktalnu dimenziju 1,2619, dok Sierpinskijev trokut približno 1,584962. Iz vrijednosti, kao i sa prethodnih slika, možemo uočiti da je trokut malo gušći od Kochove krivulje, tj. kažemo da ispunjava veći dio ravnine.

Slika 6 - Hilbertova krivulja [Wikipedia, Hilbertova krivulja, dostupno na: http://hr.wikipedia.org/wiki/Hilbertova_krivulja, učitano: 17.01.2015.]

Hilbertova krivulja je beskonačno gusta krivulja koju je opisao njemački matematičar David Hilbert 1891. god. Ona nastaje nakon beskonačno mnogo iteracija.


Slika 7 - Contorov skup [Hrvatski matematički elektronski časopis math.e, Galerija fraktala, V. Antočić, A. Galinović, dostupno na: http://e.math.hr/galerija/galerija_print.html, učitano: 17.01.2015.]

Za fraktalne skupove moguće je definirati njihove fraktalne dimenzije na nekoliko načina. Pokazuje se da je fraktalna dimenzija Cantorova skupa strogo manja od 1 i to točno jednaka log2 / log3 = 0,6309.

Kvazi samoslični fraktali su oni fraktali koji sadrže male kopije sebe koje nisu slične cijelom fraktalu, već se pojavljuju u iskrivljenom obliku, a primjere za to možemo vidjeti na donjim slikama:

Slika 8 - Juliaov skup za c = -1 [Hrvatski matematički elektronski časopis math.e, Galerija fraktala, V. Antočić, A. Galinović, dostupno na: http://e.math.hr/galerija/galerija_print.html, učitano: 17.01.2015.]

Slika 9 - Mandelbrotov skup [Hrvatski matematički elektronski časopis math.e, Galerija fraktala, V. Antočić, A. Galinović, dostupno na: http://e.math.hr/galerija/galerija_print.html, učitano: 17.01.2015.]



Statistički samoslični fraktali su fraktali koji ne sadrže kopije samoga sebe, no međutim neke njegove osobine kao što je fraktalna dimenzija ostaju iste pri različitim mjerilima. Primjer za to možemo vidjeti na slici 10.

Slika 10 – Dvodimenzionalni Perlinov šum – svjetlije nijanse predstavljaju više vrijednosti funkcije [Fractal Noise, Neil Blevins, dostupno na: http://www.neilblevins.com/cg_education/fractal_noise/fractal_noise.html, učitano: 17.01.2015.]



3.1.2.Podjela fraktala prema načinu nastanka


Kod podjele fraktala prema načinu nastanka razlikujemo:


Iterativni fraktali nastaju kopiranjem, te rotiranjem i/ili translatiranjem kopije, te mogućim zamjenjivanjem nekog elementa kopijom (npr. Kochova krivulja).

Rekurzivni fraktali su određeni rekurzivnom matematičkom formulom koja određuje pripada li određena točka prostora (npr. kompleksna ravnina) skupu ili ne.

Slučajni fraktali posjeduju najmanji stupanj samosličnosti i možemo ih zapaziti najčešće u prirodi kao što su munje, oblaci, obale ili drveće.


Slika 11 - Istra i mnogi drugi poluotoci su fraktali [Neverinov blog, dostupno na: http://blog.dnevnik.hr/blogodneverina/2010/02/1627237517/fasciniranost-fraktalima.html, učitano: 17.01.2015.]


Slika 12 - Drvo iz prirode – fraktal [Silvergreen, Deviant art, dostupno na: http://silvergreen.deviantart.com/art/Fractal-Tree-1646228, učitano: 17.01.2015.]



3.2.Primjena fraktala



Najjednostavniji primjer gdje se susrećemo s konkretnom primjenom fraktala u računalnoj grafici je crtanje terena, a posebice se to odnosi na planine. Njih možemo crtati tako da se horizontalno položenom trokutu svaki vrh snizi ili povisi za neku slučajno odabranu vrijednost. Takvom trokutu se zatim spoje polovišta stranica, te se na taj način dobivaju četiri nova trokuta. Srednjemu od njih snizimo ili povisimo vrhove kao i prvotnom trokutu, no međutim tada koristimo dvostruko manje vrijednosti. Isti postupak se nadalje ponavlja za sva četiri trokuta ispočetka. [Uvod u matematičke metode u inženjerstvu, Fraktali, dostupno na:

http://matematika.fkit.hr/novo/izborni/referati/dobrinic_joskic_brdar_fraktali.pdf, 5.str. učitano: 17.01.2015.]

Na sljedećoj slici dolje možemo vidjeti funkciju prikazanu u trodimenzionalnom prostoru. Ovdje su više vrijednosti jednostavno prikazane na višem položaju. Tako se dobiva model koji uvelike nalikuje planini.

Slika 13 - Stvorena planina uz pomoć Perlinovog šuma (WebGL Demo – Fractal Terrain Generator, dostupno na: http://www.webgl.com/2012/05/webgl-demo-fractal-terrain-generator/ , učitano: 17.01.2015.]

Ako bi uzeli u obzir sustave iteriranih funkcija u tri dimenzije, mogli bismo iscrtavati razne objekte kao što su drveće, cvijeće, grmlje, korijenje i tome slično. Ako to isto napravimo u trodimenzionalnom sustavu i na kraj svake grančice dodamo pokoji list, rezultat bi bio nevjerovatno sličan stvarnim pojavama u prirodi, baš kao što je prikazano na slici 14. Od manje bitnijih primjena tu je predviđanje stohastičkih procesa kao što su recimo potresi, slaganje snopova optičkih vlakana, oponašanje rada neuronskih mreža za razvoj umjetne inteligencije i dr. Za uređaje kao što su mobiteli, proizvode se antene u obliku fraktala koje zbog toga koriste široki spektar frekvencija, a uz to ne zauzimaju puno prostora.

Slika 14 - Trodimenzionalni fraktal – drvo [Pythagoras Tree, dostupno na: http://www.phidelity.com/blog/phidelity/blog/fractal/pythagoras-tree/, učitano: 17.01.2015.]


3.3.Fraktalna dimenzija


Fraktalna dimenzija je vrijednost koja nam daje uvid u to u kojoj mjeri nekakav fraktal ispunjava prostor u kojem se nalazi. Pronašli smo podosta definicija fraktalnih dimenzija od kojih se niti jedna nije pokazala univerzalnom. Najbolja je dimenzija samosličnosti, no međutim nju upotrebljavamo samo kod jako jednostavnih geometrijskih fraktala. Za teoriju nam je najbitnija ona Hausdorffova dimenzija, a u konkretnim slučajevima, odn. u praksi najviše se koristi Minkowski-Bouligandova dimenzija. [Uvod u matematičke metode u inženjerstvu, Fraktali, dostupno na: http://matematika.fkit.hr/novo/izborni/referati/dobrinic_joskic_brdar_fraktali.pdf, 6. str. učitano: 17.01.2015.]

Dimenzija samosličnosti koristi razne promjene mjera (primjerice dužine, površine, obujmi, ...) u odnosu na mijenjanje broja iteracija kod potpuno samosličnih fraktala. Kod Kochove krivulje svaka naredna iteracija daje četiri puta više segmenata na tri puta manje dužine. Ako bismo broj segmenata označili sa N, a dužinu segmenta s L, ukupno bi dobili dužinu krivulje NL. Možemo zatim reći da za Kochovu krivulju vrijedi: ako za mjeru dužine uvrstimo spomenuti „d-dimenzionalni metar“. Daljnjim preuređivanjem jednadžbe dobivamo ili češće . Tako bi mjerna jedinica za Kochovu krivulju bila otprilike m1.2619 . Općenito možemo reći da se dimenzija potpuno samosličnog fraktala računa po formuli .

Minkowski-Bouligandova dimenzija – ako bismo uzeli fraktal koji leži u ravnini i prekrili ga proizvoljnim brojem N sukladnih kvadrata duljine stranice A, smanjivanjem te duljine kvadrata (a samim time i povećanjem njihovog broja) promijenio bi se i broj kvadrata koji sadrže fraktal. Upravo ova metoda koristi odnos broja tih kvadrata i duljine stranica. Na taj način možemo odrediti dimenziju jednostavnih objekata, čija nam je dimenzija već poznata (razne definicije dimenzije ne bi trebale davati različite rezultate kod vrlo jednostavnih objekata) kao što su primjerice kvadrati. Dobit ćemo opću formulu za kvadrat (pri tome znamo da se radi o dvodimenzionalnom kvadratu): . Ako bismo učinili istu stvar i sa dužinom (jednodimenzionalnom), te kockom (trodimenzionalnom), dobili bi opće formule: i . Iz ovih primjera vidimo opću formulu za objekte bilo koje dimenzije , tj. . Valja istaknuti da ova metoda ne daje uvijek potuno točne rezultate, te da joj se rezultati pomiču stvarnima s povećanjem broja dužina, kvadrata i kocaka. Najviše se koristi kod određivanja fraktalne dimenzije nepravilnih objekata. [Uvod u matematičke metode u inženjerstvu, Fraktali, dostupno na: http://matematika.fkit.hr/novo/izborni/referati/dobrinic_joskic_brdar_fraktali.pdf, 7. str. učitano: 17.01.2015.]

Topološka dimenzija je ono što nazivamo još i intuitivnom dimenzijom, tj. broj smjerova u kojima bismo mogli ići da smo u određenom objektu, odnosno broj stupnjeva slobode. Na takav način je svaka linija (ravna ili zakrivljena) jednodimenzionalna, jer postoji samo jedan stupanj slobode, a to je dužina (lijevo-desno). Svaka je ploha (ravna ili savinuta) dvodimenzionalna, jer postoje dva stupnja slobode – dužina i širina (lijevo-desno i gore-dolje). Topološka dimenzija uvijek je pozitivan cijeli broj ili nula. Ako bi to sagledavali stručnije, topološka dimenzija se može definirati i kao najmanja moguća vrijednost n, tako da se bilo koji otvoreni pokrivač (pokrivač čiji su svi elementi otvoreni skupovi) može podesiti tako da svaka točka bude najviše n+1 element. Primjerice, ako želimo odrediti topološku dimenziju kružnice, konstruirat ćemo joj pokrivač od otvorenih kružnih lukova. Zatim ćemo podesiti taj pokrivač tako da smanjimo preklapanje njegovih elemenata na najmanju moguću mjeru, ali da cijela kružnica i dalje bude potpuno pokrivena. Pošto smo uzimali otvorene lukove, preklapanje je neizbježno, a može se napraviti tako da svaka točka kružnice bude dio samo jednog ili dva elementa pokrivača. Najveća je vrijednost n = 2, tako da topološka dmenzija iznosi n – 1, te zaključujemo na temelju toga da je kružnica jednodimenzionalna. Ovo matematičko objašnjenje se na prvi pogled možda čini nepotrebnim, ali je vrlo bitno u nekim elementima više matematike, kao primjerice u našoj seminarkoj temi o fraktalima. [Uvod u matematičke metode u inženjerstvu, Fraktali, dostupno na: http://matematika.fkit.hr/novo/izborni/referati/dobrinic_joskic_brdar_fraktali.pdf, 8. str. učitano:17.01.2015.]



4.Prikaz grafike s OpenGL ES


Android okvir pruža mnogo standardnih alata za stvaranje funkcionalnog grafičkog sučelja. Ako primjerice želimo postići veću kontrolu nad onime što želimo prikazati na zaslonu kao što je modeliranje u trodimenzionalnim slikama trebali bismo koristiti više vrsta različitih funkcija. OpenGL ES API od strane Android okvira nudi čitav niz alata za prikaz vrhunske animirane grafike koje su ograničene samo našom maštom i također možemo imati koristi od ubrzanja grafičke obrade jedinica (GPU) koje se nalaze na mnogim Android uređajima.


4.1.Izgradnja OpenGL ES okruženja


Kako bi mogli iscrtavati grafičke objekte uz pomoć OpenGL ES na našem Android-u moramo prvo kreirati određene poglede na spremnike za njih. Jedan od najboljih načina za to je implementacija sučelja GLSurfaceView i GLSurfaceView.Renderer. GLSurfaceView je spremnik za grafičko crtanje s OpenGL-om i GLSurfaceView-om. Renderer kontrolira ono što je nacrtano u tom pogledu. GLSurfaceView je samo jedan od načina da se u OpenGL ES uključi grafika u takav program. Primjerice za prikaz punog zaslona to je definitivno najbolji izbor.


4.1.1.Deklariranje OpenGL koristeći Manifest


Kako bi mogli koristiti OpenGL ES 2.0 API u našoj aplikaciji moramo ga deklarirati u našem manifestu sa ovim XML isječkom:


<uses-feature android:glEsVersion="0x00020000" android:required="true" />


Ako naša aplikacija koristi kompresiju teksture, također moramo deklarirati u manifestu koje formate kompresije naša aplikacija podržava. To vrijedi samo za kompatibilne uređaje. [Developers, Displaying Graphics with OpenGL ES, dostupno na: http://developer.android.com/training/graphics/opengl/environment.html, 25.01.2015.]


<supports-gl-texture android:name="GL_OES_compressed_ETC1_RGB8_texture" />
<supports-gl-texture android:name="GL_OES_compressed_paletted_texture" />

4.1.2.Kreiranje aktivnosti za OpenGL ES grafiku


Android aplikacije koje koriste OpenGL ES imaju aktivnosti baš kao i bilo koje druge aplikacije koja ima korisničko sučelje. Glavna razlika od drugih aplikacija je ono što se stavi u raspored za naše aktivnosti. Dok se u mnogim aplikacijama koriste TextView, gumb i ListView, u aplikaciji koji koristi OpenGL ES, također možete dodati i GLSurfaceView.


Sljedeći kod pokazuje minimalnu provedbu aktivnosti koje koristi GLSurfaceView kao svoj primarni pogled [Developers, Displaying Graphics with OpenGL ES, dostupno na: http://developer.android.com/training/graphics/opengl/environment.html, 25.01.2015.]:


public class OpenGLES20Activity extends Activity {

private GLSurfaceView mGLView;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

// Create a GLSurfaceView instance and set it

// as the ContentView for this Activity.

mGLView = new MyGLSurfaceView(this);

setContentView(mGLView);

}

}

4.1.3.Gradnja GLSSurfaceView objekata


GLSurfaceView je specijalizirani pogled u kojem možemo crtati grafiku OpenGL ES. To samo za sebe ne znači baš mnogo. Stvarni crtež objekata se kontrolira u GLSurfaceView.Renderer-u koje smo postavili na ovom prikazu. Naime, kod za ovaj objekt je tako malen, pa možemo biti u iskušenju da ga preskočimo ili stvorimo nemodificiranu GLSurfaceView instancu. Morali bismo proširiti ovu klasu kako bi se omogućilo snimanje događaja. Veoma važan programski kod za GLSurfaceView je minimalan, tako da za brzu provedbu, uobičajeno je da samo stvorimo unutarnje klase u aktivnosti koje koristimo [Developers, Displaying Graphics with OpenGL ES, dostupno na: http://developer.android.com/training/graphics/opengl/environment.html, 25.01.2015.]:


class MyGLSurfaceView extends GLSurfaceView {

public MyGLSurfaceView(Context context){

super(context);

// Set the Renderer for drawing on the GLSurfaceView

setRenderer(new MyRenderer());

}

}


Kada koristimo OpenGL ES 2.0, moramo dodati još jedan poziv na svoj GLSurfaceView konstruktor, te naznačimo da želimo koristiti 2.0 API [Developers, Displaying Graphics with OpenGL ES, dostupno na: http://developer.android.com/training/graphics/opengl/environment.html, 25.01.2015.]:


// Create an OpenGL ES 2.0 context

setEGLContextClientVersion(2);


Jedan drugi izborni dodatak našem GLSurfaceView-u je postavljanje načina za pogled kada je došlo do promjene crtanja podataka pomoću GLSurfaceView.RENDERMODE_WHEN_DIRTY postavke [Developers, Displaying Graphics with OpenGL ES, dostupno na: http://developer.android.com/training/graphics/opengl/environment.html, 25.01.2015.]:


// Render the view only when there is a change in the drawing data

setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);


Ova postavka sprječava GLSurfaceView okvir od toga da ponovno iscrtava dok ne pozovemo funkciju requestRender(), što je učinkovitije za ovaj uzorak.


4.1.4.Gradnja Renderer klase (klasa prikazivanja)


Implementacija GLSurfaceView.Renderer klase unutar aplikacije koja koristi OpenGL ES je dio gdje stvari počinju biti zanimljive i interesantne. Ova klasa kontrolira ono što je iscrtano u GLSurfaceView s kojim je i povezana. Postoje tri metode u renderer klasi koje se pozivaju od strane Android sustava kako bi shvatili što i kako se iscrtava na GLSurfaceView-u:

Primjer koda ispod je osnovna provedba OenGL ES prikazivača koji ne radi ništa bitno, samo postavlja sivu pozadinu u GLSurfaceView-u [Developers, Displaying Graphics with OpenGL ES, dostupno na: http://developer.android.com/training/graphics/opengl/environment.html, 25.01.2015.]:


public class MyGLRenderer implements GLSurfaceView.Renderer {

public void onSurfaceCreated(GL10 unused, EGLConfig config) {

// Set the background frame color

GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

}

public void onDrawFrame(GL10 unused) {

// Redraw background color

GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

}

public void onSurfaceChanged(GL10 unused, int width, int height) {

GLES20.glViewport(0, 0, width, height);

}

}


Kod koji smo naveli iznad je u biti jednostavna mala Android aplikacija koja prikazuje sivi zaslon pomoću OpenGL-a. Ova klasa je temelj koji je potreban za početak iscrtavanja grafičkih elemenata s OpenGL-om. Sada kada smo upoznati s OpenGL ES API-jem, možemo postaviti OpenGL ES okruženje u aplikaciji i početi crtati grafiku.


4.2.Definiranje oblika


Biti u stanju definirati oblike koji se mogu izvesti u kontekstu pogleda OpenGL ES-a je prvi korak u stvaranju visokozahtjevne grafike. Crtanje s OpenGL ES-om može biti malo teže bez poznavanja nekoliko osnovnih stvari o tome kako OpenGL ES očekuje definiranje grafičkih objekata. Ovdje ćemo objasniti kako je OpenGL ES koordinatni sustav relativan u odnosu na Android-ov zaslon uređaja, osnove definiranja oblika, oblik lica, te definiranje trokuta i kvadrata.


4.2.1.Primjer definiranja trokuta


OpenGL ES omogućuje definiranje crtanja objekata pomoću koordinata u trodimenzionalnom prostoru. Prije nego što nacrtamo trokut, moramo definirati svoje koordinate. U OpenGL, tipičan način da to učinimo je da si definiramo najvišu točku ili tjeme i brojeve s pomičnim zarezom za koordinate. Za maksimalnu učinkovitost, pišemo ove koordinate u ByteBuffer, koji je uspješno prošao u OpenGL ES grafički cjevovod za obradu.

[Developers , Defining Shapes, dostupno na: http://developer.android.com/training/graphics/opengl/environment.html, 25.01.2015.]



public class Triangle {

private FloatBuffer vertexBuffer;

// number of coordinates per vertex in this array

static final int COORDS_PER_VERTEX = 3;

static float triangleCoords[] = { // in counterclockwise order:

0.0f, 0.622008459f, 0.0f, // top

-0.5f, -0.311004243f, 0.0f, // bottom left

0.5f, -0.311004243f, 0.0f // bottom right

};

// Set color with red, green, blue and alpha (opacity) values

float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };

public Triangle() {

// initialize vertex byte buffer for shape coordinates

ByteBuffer bb = ByteBuffer.allocateDirect(

// (number of coordinate values * 4 bytes per float)

triangleCoords.length * 4);

// use the device hardware's native byte order

bb.order(ByteOrder.nativeOrder());

// create a floating point buffer from the ByteBuffer

vertexBuffer = bb.asFloatBuffer();

// add the coordinates to the FloatBuffer

vertexBuffer.put(triangleCoords);

// set the buffer to read the first coordinate

vertexBuffer.position(0);

}

}


Po početnim vrijednostima, OpenGL ES pretpostavlja koordinatni sustav [0,0,0] (X, Y, Z) i specificira središte GLSurfaceView okvir. [1,1,0] je gornji desni kut okvira i [- 1, -1,0] je u donjem lijevom kutu okvira.


4.2.2.Primjer definiranja kvadrata


Definiranje trokuta je prilično lako u OpenGL, ali što ako želimo da nešto malo složenije? U ovom primjeru konkretno mislimo na kvadrat. Postoji nekoliko načina za iscrtavanje kvadrata, ali tipičan način izrade takvog oblika u OpenGL ES-u je koristiti dva trokuta nacrtana zajedno kao što je prikazano na slici ispod.

Slika 15 - Crtanje kvadrata uz pomć 2 trokuta [Defining Shapes, dostupno na: http://developer.android.com/training/graphics/opengl/shapes.html, učitano: 25.01.2015.]


Opet bi trebali definirati točke u suprotnom smjeru od kazaljke na satu kako bi oba trokuta predstavljali ovaj oblik, te postaviti vrijednosti u ByteBuffer. Kako bi se izbjeglo da dvije koordinate dijele svaki trokut dva puta, koristimo se popisom za crtanje koji daje upute OpenGL ES-u na način kako da grafički cjevovod izvuće te vrhove. Pogledajmo kod za kvadrat [Developers, Defining Shapes, dostupno na: http://developer.android.com/training/graphics/opengl/environment.html, 25.01.2015.]:


public class Square {

private FloatBuffer vertexBuffer;

private ShortBuffer drawListBuffer;

// number of coordinates per vertex in this array

static final int COORDS_PER_VERTEX = 3;

static float squareCoords[] = {

-0.5f, 0.5f, 0.0f, // top left

-0.5f, -0.5f, 0.0f, // bottom left

0.5f, -0.5f, 0.0f, // bottom right

0.5f, 0.5f, 0.0f }; // top right

private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices

public Square() {

// initialize vertex byte buffer for shape coordinates

ByteBuffer bb = ByteBuffer.allocateDirect(

// (# of coordinate values * 4 bytes per float)

squareCoords.length * 4);

bb.order(ByteOrder.nativeOrder());

vertexBuffer = bb.asFloatBuffer();

vertexBuffer.put(squareCoords);

vertexBuffer.position(0);

// initialize byte buffer for the draw list

ByteBuffer dlb = ByteBuffer.allocateDirect(

// (# of coordinate values * 2 bytes per short)

drawOrder.length * 2);

dlb.order(ByteOrder.nativeOrder());

drawListBuffer = dlb.asShortBuffer();

drawListBuffer.put(drawOrder);

drawListBuffer.position(0);

}

}


Ovaj primjer daje nam ono što je potrebno za stvaranje složenijih oblika s OpenGL-om. Općenito, koristimo zbirke trokuta za crtanje objekata.


4.3.Crtanje oblika


Nakon što smo prikazali oblike koji se mogu izvesti s OpenGL, poželjet ćemo ih i iscrtati. Crtanje oblika s OpenGL ES 2.0 traje malo više koda, jer API omogućuje veliku kontrolu nad slikama koji pruža cjevovod.

4.3.1.Inicijalizacija oblika


Prije nego bilo što nacrtamo, moramo inicijalizirati i učitati oblike koje namjeravamo nacrtati. Osim ako strukturu (izvornih koordinata) oblika koje koristimo u svom programu promjenimo tijekom izvršenja, trebali bismo ih inicijalizirati u onSurfaceCreated() metodi našeg renderer-a (prikazivača) za pamćenje i učinkovitost obrade. [Developers, Drawing Shapes, dostupno na: http://developer.android.com/training/graphics/opengl/environment.html, 25.01.2015.]:


public void onSurfaceCreated(GL10 unused, EGLConfig config) {

...

// initialize a triangle

mTriangle = new Triangle();

// initialize a square

mSquare = new Square();

}


4.3.2.Crtanje oblika


Crtanjem definiramo oblik pomoću OpenGL ES-a 2.0 i to zahtijeva značajnu količinu koda, jer moramo pružiti puno detalja u grafici pruzujući sve kroz cjevovod. Moramo definirati sljedeće:

Shader je računalni program koji se koristi za napraviti sjenčanje.

Trebamo barem jedan vrh osjenčati kako bi nacrtali oblik i jedan fragment osjenčati bojom da bi prikazali oblik. To sjenčanje mora biti iskompajlirano, a zatim je dodano u OpenGL ES program, koji se potom koristi za iscrtavanje oblika. Ovdje je primjer kako definirati osnovno sjenčanje koje možemo koristiti za crtanje oblika [Developers, Drawing Shapes, dostupno na: http://developer.android.com/training/graphics/opengl/environment.html, 25.01.2015.].


private final String vertexShaderCode =

"attribute vec4 vPosition;" +

"void main() {" +

" gl_Position = vPosition;" +

"}";

private final String fragmentShaderCode =

"precision mediump float;" +

"uniform vec4 vColor;" +

"void main() {" +

" gl_FragColor = vColor;" +

"}";


Shaderi ili zasjenjivači sadrže OpenGL sjenčanje jezika (GLSL) kod kojeg se mora iskompajlirati prije upotrebe u OpenGL ES okruženju. Kako bi mogli nacrtati svoje oblike, moramo prvo sastaviti Shader šifru, dodati ga u OpenGL ES programski objekt, a zatim povezati program. To možemo napraviti u našem nacrtanom objektu konstruktora, pa je to dovoljno učiniti samo jednom.

Kompajliranje OpenGL ES shadera i povezivanja programa je skuplji u odnosu na CPU ciklus i vrijeme obrade, tako da treba izbjegavati to više puta. Ako ne znamo sadržaj naših shader-a ili zasjenjivača tijekom instalacije, trebali bismo napraviti svoj kod, dovoljno ga je samo jednom kreirati i spremiti za kasniju uporabu [Developers, Drawing Shapes, dostupno na: http://developer.android.com/training/graphics/opengl/environment.html, 25.01.2015.].


public class Triangle() {

...

int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);

int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

mProgram = GLES20.glCreateProgram(); // create empty OpenGL ES Program

GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program

GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program

GLES20.glLinkProgram(mProgram); // creates OpenGL ES program executables

}


4.4.Primjena projekcije i pogled kamere


U OpenGL ES okruženju, projekcije i pogled kamere omogućuju prikazivanje nacrtanih objekata na način da više fizički sliče kao oni iz stvarnosti. Ova simulacija fizičkog pogleda je načinjena s matematičkim transformacijama nacrtanih objekata uz pomoć koordinata:


4.4.1. Definiranje projekcije


Podaci za transformaciju projekcijom se izračunavaju onSurfaceChanged() metodom naše GLSurfaceView.Renderer klase. Sljedećim kodom uzimamo visinu i širinu GLSurfaceView i koristimo ga za popunjavanje matrice projekcije transformacije pomoću Matrix.frustumM () metode [Developers, Applying Projection and Camera Views, dostupno na: http://developer.android.com/training/graphics/opengl/environment.html, 25.01.2015.]:

@Override

public void onSurfaceChanged(GL10 unused, int width, int height) {

GLES20.glViewport(0, 0, width, height);

float ratio = (float) width / height;

// this projection matrix is applied to object coordinates

// in the onDrawFrame() method

Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);

Ovaj kod popunjava matricu projekcija, mProjectionMatrix koje možemo kombinirati s transformacijom kamera u onDrawFrame() metodi.


4.4.2. Definiranje pogleda kamere


Kompletan proces transformacije nacrtanih objekata završava dodavanjem transformacija u pogled kamere kao dio procesa crtanja. U sljedećem primjeru koda, transformacija pogleda kamere se izračunava pomoću Matrix.setLookAtM() metode, a zatim u kombinaciji s prethodno izračunatom projekcijom matrice [Developers, Applying Projection and Camera Views, dostupno na: http://developer.android.com/training/graphics/opengl/environment.html, 25.01.2015.].


public void draw(float[] mvpMatrix) { // pass in the calculated transformation matrix

...

// get handle to shape's transformation matrix

mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");

// Pass the projection and view transformation to the shader

GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);

// Draw the triangle

GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);

...

}


4.5. Dodavanje pokreta


Crtanje objekata na zaslonu je temeljna značajka OpenGL, ali možemo to učiniti s drugim Android grafičkim okvirima klasa, uključujući Canvas i Drawable objekte. OpenGL ES nudi dodatne mogućnosti za kretanje i pretvaranje nacrtanih objekata u tri dimenzije ili druge jedinstvene načine za kreiranje korisnikovih iskustava.

4.5.1.Rotiranje oblika


Rotirajunje objekata s OpenGL ES 2.0 je relativno jednostavno. Možemo jednostavno stvoriti drugu matricu transformacije (matricu rotacije), a zatim je kombinirati sa svojim projekcijama i pogledom kamere matricom transformacija [Developers, Adding Motion,dostupno na: http://developer.android.com/training/graphics/opengl/environment.html, 25.01.2015.]:


private float[] mRotationMatrix = new float[16];

public void onDrawFrame(GL10 gl) {

...

float[] scratch = new float[16];

// Create a rotation transformation for the triangle

long time = SystemClock.uptimeMillis() % 4000L;

float angle = 0.090f * ((int) time);

Matrix.setRotateM(mRotationMatrix, 0, angle, 0, 0, -1.0f);

// Combine the rotation matrix with the projection and camera view

// Note that the mMVPMatrix factor *must be first* in order

// for the matrix multiplication product to be correct.

Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);

// Draw triangle

mTriangle.draw(scratch);

}

4.5.2. Omogućavanje kontinuiranog prikazivanja (renderiranja)


Ako se slučajno naš oblik ne okreće, moramo komentirati u kodu dio s postavkom GLSurfaceView.RENDERMODE_WHEN_DIRTY. OpenGL inače rotira oblik inkrementalno i čeka poziv funkcije requestRender() iz GLSurfaceView spremnika [Developers, Adding Motion, dostupno na: http://developer.android.com/training/graphics/opengl/environment.html, 25.01.2015.]:


public MyGLSurfaceView(Context context) {

...

// Render the view only when there is a change in the drawing data.

// To allow the triangle to rotate automatically, this line is commented out:

//setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);

}

Osim ako imamo objekte koji se mijenjaju bez bilo kakve interakcije s korisnikom, onda je dobra ideja imati ovu zastavicu podignutu ili uključenu.

5. Prikaz grafike s OpenGL ES iz Processinga 2.0


Kako Android platforma sadrži potpunu podršku za Java okruženje u prethodnom poglavlju obradili smo OpenGL ES iz perspektive Jave. No, Android je i ujedno open source ekosustav, što znači da Java nije jedini alat koji nam stoji za manipulaciju OpenGL ES-a. Na Android platformi možemo pristupiti OpenGL ES-u iz bilo kojeg skriptnog jezika putem Android SL4A ili nekog trećeg ekosustava kao što je to Processing 2.0.


Kako bi učinili cijelokupnu stvar nama zanimljivijom, pitali smo se kako upravljati OpenGL ES-om iz neke druge perspektive koja nije Javina, istraživanjem došli smo do 2 odgovora. Prvi je, da je to moguće uz pomoć C/C++ jezika kojeg ćemo ugraditi u Java aplikaciju putem JNI sučelja. Drugi je, putem Processing 2.0 API-a. Naime, Processing je veoma popularan jezik i okruženje za programiranjem sa velikim naglaskom na računalnu grafiku i grafičke elemente. Kada smo vidjeli da Processing 2.0 sadrži podršku za Android grafiku putem OpenGL ES-a odlučili smo dalje istražiti tu perspektivu razvoja.


U ovom poglavlju napraviti ćemo kratak pregled u OpenGL ES iz perspektive Processing 2.0 jezika. U prethodnim poglavljima razmatrali smo kako je OpenGL ES zapravo okvir za koji je zadužna grafička kartica na uređaju (eng. Graphic Processing Unit, u nastavku GPU). Upravo taj okvir čini direktan način programiranja 3D grafike na Androidu. OpenGL ES je također API koji je namjenjen da se koristi na više platformi, a ne samo na Androidu, te ujedno čini i podskup OpenGL-a. Minimalni hardwerski zahtjevi da pokrenemo neku 3D grafiku na Androidu iz Processinga su sljedeći:


Kada radimo Processing skicu za Android dostupna su nam 2 stroja renderiranja. Prvi je sam OpenGL ES, dok je drugi Android 3D (u nastavku A3D). Cijelokupan Processing na Androidu koristi OpenGL ES direktno, i sve skice koje izradimo će u runtime-u koristiti OpenGL ES 2.0. Stroj renderiranja u Processingu je zapravo modul koji je zadužan za sva crtanja i sve operacije sa kojima crtamo. Stroj renderiranja specificiramo kada stvaramo objekt ekrana i zadajemo mu rezoluciju. U slučaju ako ne zadamo neki stroj renderiranja Processing će upotrebljavati A2D. U nastavku ćemo razmotriti neke od scenarija korištenja Processinga na Androidu te uvidjeti da je zapravo puno intuitivnije za koristiti Processingom API za crtanje nego čiste pozive OpenGL ES-a iz Jave.


Prije nego se upustimo u naprednije stvari iz Processinga razmotrimo primjer kako crtati na platno koje nećemo prikazati na ekranu (eng. Offscreen drawing). Da bi izradili platno u Processingu potrebno je pozvati se na createGraphics() metodu.


PGraphicsAndroid3D pg;

void setup() {

size(480, 800, A3D);

pg = createGraphics(300, 300, A3D);

...

}


Ovakav crtež koji se ne prikazuje na ekranu, kasnije možemo iskoristi kao sliku za teksturu objekta ili da kombiramo sa drugim slojevima. Naprimjer, ovako:


void draw() {

pg.beginDraw();

pg.rect(100, 100, 50, 40);

pg.endDraw();

...

cube.setTexture(pg.getOffscreenImage());

...

}



5.1. Geometrijske transformacije


Koordinatni sustav u Processingu definiran je sa X osi koja se proteže od lijeve prema desnoj strani, Y os od doljnoj prema gornjoj strani i negativna Z os pokazuje od ekrana. Kada bi gledali ishodište koordinatnog sustava ono bi bilo u gornjom lijevom kutu. Ovo je važno da znamo jer sve geometrijske transformacije koje ćemo primjeniti nad našim objektima će se odnositi na cijeli koordinatni sustav. Iz slike 16. možemo vidjeti kako je koordinatni sustav za 3D implementiran u Processingu.


Slika 16 Koordinatni sustav kako je implementiran u Processingu


5.1.1. Translacija


Translacija se vrši uz pomoć translate(dx, dy, dz) funkcije.


void setup() {

size(240, 400, A3D);

stroke(255, 150);

}


void draw() {

background(0);

translate(50, 50, 0);

noStroke();

fill(255, 200);

rect(60, 0, 100, 100);

}


Iz slike 17. možemo vidjeti kako će gornji kod napraviti translaciju rect-a.


Slika 17 Primjer translacije


5.1.2. Rotacija


Rotacija uvijek sadrži jednu od osi oko koje objekt rotira. Ta os može biti koordinatna ili može biti neki proizvoljni vektor.


rotateX(angle), rotateY(angle), rotateZ(angle), rotate(angle, vx, vy, vz)

void setup() {

size(240, 400, A3D);

stroke(255, 150);

}

void draw() {

background(0);

rotateZ(PI / 4);

noStroke();

fill(255, 200);

rect(60, 0, 100, 100);

}



Iz doljnje slike možemo vidjeti kako gornji isječak koda će rotirati rect.


Slika 18 Prikaz rotacije





5.1.3. Skaliranje


Skaliranje može biti uniformno, znači da se objekt proširuje u svim smjerovima za određenu jednaku duljinu, tj. za neki faktor. Ili možemo definirati funkciji scale(sx, sy, sz) zasebne vrijednosti za svaki smjer.


void setup() {

size(240, 400, A3D);

stroke(255, 150);

}

void draw() {

background(0);

scale(1.5, 3.0, 1.0);

noStroke();

fill(255, 200);

rect(60, 0, 60, 60);

}


Iz gornjeg isječka, naš pravokutnik će se skalirati kako je prikazano na sljedećoj slici.

Slika 19 Primjer skaliranja u Processingu



5.2. Kamera i perspektiva


Konfiguriranje pogleda sceneu A3D zahtjeva konfiguraciju lokaciju kamere i volumena gledanja. Postavljanje kamere je specificirano pozicijom oka, centrom scene te koja koordinatna os gleda prema gore. Funkcija za konfiguraciju kamere u Processingu je:


camera(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);


Ako se gore definirana funkcija ne poziva, A3D automatski će ju pozvati sa defaltnim vrijednostima koje su:


camera(width / 2.0, height / 2.0, (height / 2.0) / tan(PI * 60.0 / 360.0), width / 2.0, height / 2.0, 0, 0, 1, 0);


Razmotrimo sada primjer postavljanja kamere.


void setup() {

size(240, 400, A3D);

fill(204);

}

void draw() {

lights();

background(0);

camera(30.0, mouseY, 220.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

noStroke();

box(90);

stroke(255);

line(-100, 0, 0, 100, 0, 0);

line(0, -100, 0, 0, 100, 0);

line(0, 0, -100, 0, 0, 100);

}


Gornji programski isječak nacrtati će sljedeću grafiku iz koje možemo vidjeti jednostavno kako smo zapravo postavili kameru u prostor.


Slika 20 Primjer kamere u prostoru


Kako bi napravili pogled iz određene perspekitve koristimo funkciju perspecitve. Volumen pogleda u tom slučaju je smanjena ili povećana piramida te konvergencija nacrtanih linija prema oku stvara perspektivnu projekciju gdje objekti koji su smješteni dalje će se činiti manji, dok oni bliže će se činiti većima.


Slika 21 Slika objašnjava elemente koji su uključeni u perspektivnu geometriju



5.3. Stvaranje 3D objekata


Processing je veoma jednostavan za korištenje, te stvaranje 3D objekata je puno jednostavnije, brže i intuitivnije nego je to recimo u Javi. On nam definira u A3D već određene predefinirane objekte poput sfere i kocke. Kako stvarati 3D objekte je najlakše objasniti na primjeru. Primjer jednostavnosti upotrebe jednostavnih grafičkih primitiva nalazi se u nastavku.


void setup() {

size(240, 400, A3D);

stroke(0);

}

void draw() {

background(0);

translate(width/2,height/2,0);

fill(200, 200);

pushMatrix();

rotateY(frameCount*PI/185);

box(150, 150, 150);

popMatrix();

fill(200, 40, 100, 200);

pushMatrix();

rotateX(-frameCount*PI/200);

sphere(50);

popMatrix();

}


Ovaj programski isječak prikazuje nam kako koristiti neke predefinirane grafičke primitive. Kada ga iskompajliramo i pokrenemo na Android uređaju ili emulatoru, dobiti ćemo prikaz koji se nalazi na idućoj slici.


Slika 22 Primjer korištenja predefiniranih 3d grafičkih primitiva


Drugi način stvaranja 3D objekata koji nisu predefinirani je putem beginShape(), endShape() konstrukata. Ove dvije funkcije omogućavaju nam da stvorimo složenije objekte tako da specificiramo vrhove i način njihovog spajanja (te opcionalno možemo specificirati i normale i koordinata tekstura za svaki verteks). Ova funkcionalnost je dostupna i u A2D sa malom razlikom da u A3D imamo jednu koordinatnu os više pa i za nju moramo specificirati koordinate. Naprimjer, razmotrimo sljedeći isječak Processinga.


beginShape(TRIANGLE_STRIP);

vertex(30, 75, 0);

vertex(40, 20, 0);

vertex(50, 75, 0);

vertex(60, 20, 0);

vertex(70, 75, 0);

vertex(80, 20, 0);

vertex(90, 75, 0);

endShape();


Za ovaj složeni objekt pod nazivom „TRIANGLE_STRIP“ dobiti ćemo sliku 23. kada ga renderiramo.


Slika 23 Renderiranje složenih objekata


5.3.1. Teksture


Teksture su jedna od najvažnijih tehnika u računalnoj grafici koje se sastoje od korištenja slike kao papira u koji ćemo omotati neki 3D objekt sa ciljem da simuliramo da taj objekt je načinjen od nekog materijala, da dobijemo realističnu površinu objekta ili možda neke efekte.


Slika 24 Slika objašnjava koncept tekstura u računalnoj grafici


Mapiranje tekstura na objekte je izazovan i kompleksan problem kada trebamo pridodati teksturu nekom kompliciranom 3D objektu (koji još k tome je organske prirode). Da bi pronašli pravilno mapiranje 2D slike u 3D objekt zahtjeva precizne matematičke tehnike koje u svojim izračunima uzimaju u obzir rubove, presjeke, itd. Ako razmotrimo sliku 25. vidimo koliko zapravo 3D objekt može biti kompleksan.


Slika 25 Slika demonstrira primjerom kako proces mapiranja tekstura može biti složen za određene 3D objekte


Kako bi u Processingu iskoristili teksture, pokazati ćemo to na jednostavnom primjeru kako se to radi.



PImage img1, img2;


void setup() {

size(240, 240, A3D);

img1 = loadImage("beach.jpg");

img2 = loadImage("peebles.jpg");

textureMode(NORMAL);

noStroke();

}


void draw() {

background(0);

beginShape(TRIANGLES);

texture(img1);

vertex(0, 0, 0, 0, 0);

vertex(width, 0, 0, 1, 0);

vertex(0, height, 0, 0, 1);

texture(img2);

vertex(width, 0, 0, 1, 0);

vertex(width, height, 0, 1, 1);

vertex(0, height, 0, 0, 1);

endShape();

}

Kada kompajliramo gornji primjer i pokrenemo ga na Androidu dobivamo rezultat sa sljedeće slike.


Slika 26 Primjer rada sa teksturama u Processingu


Iz gornjeg primjera vidimo kako raditi sa teksturama u Processingu na Androidu. Također, prikazali smo još jedno važno svojstvo, a to je da sa beginShape i endShape u A3D možemo iskoristiti kombinaciju više tekstura za različite dijelove jednog objekta.


5.3.2. Svijetlo


Android 3D također pruža lokalni model osvijetljenja baziran na OpenGL modelu. To je jednostavan pravovremeni (eng. realtime) model osvijetljenja, gdje svaki izvor svijetlosti sadrži 4 komponente:

Zbroj tih 4 komponenata čini total. Ovaj model ne dopušta stvaranje sjena te može definirati i do 8 različitih izvora svijetla. Odgovarajući izračuni za svijetlo zahtjevaju da se specificira normala objekta. Android 3D podržava nekoliko tipova svijetla, a to su:

Slika 27 Primjer ambijentalnog svijetla

Slika 28 Primjer direkcionalnog svijetla


Slika 29 Primjer točkastog svijetla


Slika 30 Primjer pjegastog svijetla













6. Kochova pahulja u 3D


Sada kada smo napravili kratak pregled kroz osnove OpenGL ES-a na Androidu iz dvije različite perspektive, pokušati ćemo izmodelirati Kochovu pahulju u 3D-u. Kako više naginjemo novijem pristupu razvoja Android grafike, koristiti ćemo Processing 2.0 za crtanje našeg 3D fraktala.


Za početak prisjetimo se kako se crta Kochova pahulja u 2D-u. Iz teorije znamo da se Kochova pahulja crta uz pomoć Kochove krivulje, a Kochova krivulja se dobiva tako da zadani vektor podijelimo prvo na 3 jednaka dijela, te zatim obrišemo srednji dio te na njegovom mjestu nacrtamo dva vektora koja se dodiruju u normali početnog vektora. Ovaj proces ponavljamo rekurzivno za svaki od vektora koji imamo na platnu. Kochova krivulja počinje od 1 vektora i njega rekurzivno dijeli, te na taj način tvori fraktal dok Kochova pahulja za početni oblik uzima trokut, te cijelokupni postupak iz Kochove krivulje ponavlja za svaku od stranica trokuta.


No pitanje se sada postavlja na koji način možemo nacrtati Kochovu pahulju u 3D-u. Da bi cijelu priču iz 2D-a pretvorili u 3D moramo zamijeniti primitivne konstrukte. Jednostavnim i brzim istraživanjem otkrili smo da Kochovu pahulju u 3D-u možemo nacrtati na dva načina.


Prvi način je da našu pahulju krenemo crtati od piramide. Na svaku stranicu početne piramide nacrtati ćemo novu umanjenu piramidu. Na svaku od nacrtanih piramida nacrtati ćemo po još jednu piramidu, itd. Ovim postupkom dobiti ćemo Kochovu pahulju kao što je prikazana na sljedećoj slici.


Slika 31 Prvi način crtanja Kochove pahulje [3D Koch Snowflake, dostupno na: http://blog.3dvision.com/2008/10/30/3d-koch-snowflake/]


Drugi način crtanja Kochove pahulje u 3D također kreće od piramide kao baze. No u ovom slučaju pojedinu stranicu piramide ćemo podijeliti na 4 jednakostranična trokuta te na srednjem izgraditi piramidu. U idućem koraku rekurzije to činimo za svaku stranicu trokuta uključujući i novonastale trokute.


Slika 32 Drugi način crtanja Kochove pahulje [3D Koch snowflake, Dostupno na: https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcT7GePIlCD-sSHFmcFl3OgYiojQo4rovsUVxT2YS7rEgozfG6AjUA]


S obzirom da nam drugi način crtanja je bio mnogo zanimljiviji od prvog odlučili smo se implementirati drugi način. Uvidjeli smo da takvim crtanjem Kochove pahulje u 3D, gdje pojedine stranice dijelimo na isti način kao što i crtamo Sierepinski trokut u 2D dobivamo mnogo zanimljiviji i bogatiji oblik same pahulje. U nastavku ćemo razmotriti Android Processing 2.0 kod naše implementacije Kochove pahulje.




6.1. Analiza izvornog koda


U ovom potpoglavlju analizirati ćemo naš izvorni kod.



import processing.opengl.*;



PrintWriter logger;

float pisquared = TWO_PI;

PImage tex;

FraktalniTrokut koch[]=new FraktalniTrokut[4];


void init_koch(PVector a, PVector b, PVector c, PVector d, int dubina_rekurzije) {

koch[0] = new FraktalniTrokut(a, b, c, dubina_rekurzije);

koch[1] = new FraktalniTrokut(a, d, b, dubina_rekurzije);

koch[2] = new FraktalniTrokut(a, c, d, dubina_rekurzije);

koch[3] = new FraktalniTrokut(b, d, c, dubina_rekurzije);


}


void setup() {

logger = createWriter("calculations.txt");

//stroke(0);

//strokeWeight(1);

size(displayWidth, displayHeight, OPENGL); // OPO velicina

noStroke();

//fill(150,150,150,20);

//textureMode(NORMALIZED);

fill(255,0,0,120);

//noFill();

int tockaA = 200;

int dubina_rekurzije = 2;

PVector A = new PVector(-tockaA, 0, 0);

PVector B = new PVector(tockaA/2, 0, -175);

PVector C = new PVector(tockaA/2, 0, 175);

PVector D = new PVector(0, -tockaA * (sqrt(6) / 3), 0);

init_koch(

A, B, C, D,

dubina_rekurzije

);

}


void setup_camera() {

float tockaX=(mouseX / float(width)) * pisquared;

float pozicija_x=cos(tockaX);

float pozicija_y=sin(tockaX);

float radius=300.000;

camera(pozicija_x * radius, mouseY, pozicija_y*radius, // pogled iz X, pogled iz Y, pogled iz Z

0.0, -50.0, 0.0, // center X osi, center Y osi, center Z osi

0.0, -1.0, 0.0);

}


void draw() {

background(mouseY * (255.0/600), 255, 0);

lights();

//setup_camera();

pushMatrix();

translate(displayWidth / 2, displayHeight / 2);

//translate(nf(ax, 1, 2), nf(ay, 1, 2));

rotateX(mouseY * 0.01);

rotateY(mouseX * 0.01);

print("Rotated X: " + mouseY * 0.01);

print("Rotated Y: " + mouseX * 0.01);

scale(1);

for (int i=0;i<koch.length;i++) {

koch[i].display();

}

popMatrix();

}


class FraktalniTrokut {

PVector PointA;

PVector PointB;

PVector PointC;

FraktalniTrokut podijeljeni_trokuti[] = new FraktalniTrokut[6];

int rec;

float skaliranje;

FraktalniTrokut(PVector A,PVector B,PVector C, int recursion){

skaliranje = 0.7;

PointA = A;

PointB = B;

PointC = C;

rec = recursion;

rekurzija ();

}

void rekurzija () {

if (rec != 0) {

// racunamo 3 nova vektora koji dijele povrsinu

PVector PointAB2 = PVector.add(PointA,PointB);

PointAB2.div(2);

PVector PointAC2 = PVector.add(PointA,PointC);

PointAC2.div(2);

PVector PointBC2 = PVector.add(PointB,PointC);

PointBC2.div(2);

// racunamo sredisnju tocku na danom trokutu

PVector PointZ = PVector.add(PointA,PointB);

PointZ.add(PointC);

PointZ.div(3);

// racunamo vektor smjera normale u kojoj ce se spajati trokuti

PVector PointAB = PVector.sub(PointA,PointB);

PVector PointAC = PVector.sub(PointA,PointC);

PVector PointH = PointAB.cross(PointAC);

PointH.normalize(); // normalizamo vektor na velicinu 1

// racunamo tocku u kojoj ce se spajati novi trokuti

PVector PointAAB2 = PVector.sub(PointA,PointAB2); // ovo je polovica baznog vektora koji cini stranicu trokuta kojeg dijelimo

float a = PointAAB2.mag(); // racunamo velicinu tog vektora

float pheight = a * (sqrt(8) / 3) * skaliranje; // racunamo visinu za novu piramidu, te prilagodavamo visinu tako da novi trokuti nebudu preveliki

PointH.mult(-pheight); // dizemo tocku sa povrsine u zrak

PVector PointZH = PVector.add(PointZ,PointH);

podijeljeni_trokuti[0] = new FraktalniTrokut(PointA,PointAB2,PointAC2,rec-1); // crtamo trokut 1

podijeljeni_trokuti[1] = new FraktalniTrokut(PointB,PointBC2,PointAB2,rec-1); // crtamo trokut 2

podijeljeni_trokuti[2] = new FraktalniTrokut(PointC,PointAC2,PointBC2,rec-1); // crtamo trokut 3

podijeljeni_trokuti[3]=new FraktalniTrokut(PointZH,PointAC2,PointAB2,rec-1); // crtamo trokut 4

podijeljeni_trokuti[4]=new FraktalniTrokut(PointZH,PointAB2,PointBC2,rec-1); // crtamo trokut 5

podijeljeni_trokuti[5]=new FraktalniTrokut(PointZH,PointBC2,PointAC2,rec-1); // crtamo trokut 6

}

}

void display () {

if (rec==0) {

beginShape();

texture(tex);

vertex(PointA.x, PointA.y ,PointA.z);

vertex(PointB.x, PointB.y ,PointB.z);

vertex(PointC.x, PointC.y ,PointC.z);

endShape(CLOSE);

} else {

for (int i=0; i<podijeljeni_trokuti.length;i++) {

podijeljeni_trokuti[i].display();

}

}

}

}






















6.2. Analiza različitih dubina rekurzije


Dubina rekurzije: 7


Slika 33 Kochova Pahulja u 3D sa dubinom rekurzije 7


Dubina rekurzije: 6

Slika 34 Kochova Pahulja u 3D sa dubinom rekurzije 6


Dubina rekurzije: 5

Slika 35 Kochova Pahulja u 3D sa dubinom rekurzije 5


Dubina rekurzije: 4

Slika 36 Kochova Pahulja u 3D sa dubinom rekurzije 4


Dubina rekurzije: 3

Slika 37 Kochova Pahulja u 3D sa dubinom rekurzije 3


Dubina rekurzije: 2


Slika 38 Kochova Pahulja u 3D sa dubinom rekurzije 2






7. Zaključak




U ovom seminarskom radu obrađivali smo u sklopu kolegija računalne grafike temu pod nazivom fraktali. Tema je obrađena na dva načina. Prvi je s teorijskog stajališta općenito o fraktalima kako bi ostale studente mogli upoznati i povezati s njihovim značenjem i tematikom, a drugi je praktične prirode gdje smo analizirali - OpenGL ES iz dvije perspektive – Java i Processing.

Fraktali predstavljaju u biti objekte koji daju jednaku razinu detalja neovisno o razlučivosti koju koristimo, a njihova osnovna svojstva su samosličnost, fraktalna dimenzija i oblikovanje iteracijom kao što smo i ranije naveli. Moguća je njihova podjela prema stupnju samosličnosti i prema načinu nastanka.

Fraktalna umjetnost relativno je mlada u usporedbi s drugim umjetnostima, jer ne bi mogla nastati bez uporabe računala, no fraktali su ipak s nama oduvijek. Najvjerovatnije se upravo iz tih razloga ljudima i sviđaju. U njima pronalazimo nekakvu neobičnu ljepotu, volimo gledati u njih, djeluju na nas umirujuće. Nalazimo ih posvuda u prirodi, pa i na nama samima (ako malo bolje promotrimo ruku i šaku, vidjet ćemo da je svaki prst u omjerima umanjena verzija ruke). Ljudi su ih intuitivno osjećali i prepoznavali kao načelo stvaranja prirode, pa onda nije ni čudo da u povijesti umjetnosti nalazimo fraktale ne samo prije prvih računala, nego i prije struje i parnoga stroja [Fraktali i umjetnost, Vesna Mišljenović, Zagreb (2011./2012. br. 80), str. 224.]



















Literatura



  1. Developers-Android, Displaying Graphics with OpenGL ES, dostupno na: http://developer.android.com/training/graphics/opengl/index.html, 25.01.2015. ;

  2. Fractal Noise, Neil Blevins, dostupno na: http://www.neilblevins.com/cg_education/fractal_noise/fractal_noise.html, učitano: 17.01.2015. ;

  3. Hrvatski matematički elektronski časopis math.e, Galerija fraktala, V. Antočić, A. Galinović, dostupno na: http://e.math.hr/galerija/galerija_print.html, učitano: 17.01.2015. ;

  4. Interaktivna računalna grafika kroz primjere u OpenGL-u, M.Čupić, Ž.Mihajlović, dostupno na: http://www.zemris.fer.hr/predmeti/irg/knjiga.pdf, učitano: 17.01.2015. ;

  5. M. Pašić, Uvod u matematičku teoriju kaosa za inženjere, Skripta FER, Zagreb, 2005. (57.-83.) ;

  6. Neverinov blog, dostupno na: http://blog.dnevnik.hr/blogodneverina/2010/02/1627237517/fasciniranost-fraktalima.html, učitano: 17.01.2015. ;

  7. Processing for Android, dostupno na: http://processing.flosscience.com/processing-for-android, 25.01.2015. ;

  8. Processing/processing-android, Joel Moniz, dostupno na: https://github.com/processing/processing-android/wiki, 25.01.2015. ;

  9. Processing & Android: Mobile app development made (very) easy, dostupno na: http://blog.blprnt.com/blog/blprnt/processing-android-mobile-app-development-made-very-easy, 25.01.2015.

  10. Pythagoras Tree, dostupno na: http://www.phidelity.com/blog/phidelity/blog/fractal/pythagoras-tree/, učitano: 17.01.2015. ;

  11. Shane Bow The Chaos of Mandelbrot, dostupno na: http://shanebow.com/projects/mandelbrot/, učitano: 17.01.2015. ;

  12. Silvergreen, Deviant art, dostupno na: http://silvergreen.deviantart.com/art/Fractal-Tree-1646228, učitano: 17.01.2015. ;

  13. Uvod u fraktale, M.Paušić, dostupno na: http://www.fer.unizg.hr/_download/repository/Uvod%20U%20Fraktale%20by%20Mladen%20Pausic.pdf, učitano: 17.01.2015. ;

  14. Uvod u matematičke metode u inženjerstvu, K. Brdar, M. Dobrinić, R. Joksić, Fraktali, dostupno na: http://matematika.fkit.hr/novo/izborni/referati/dobrinic_joskic_brdar_fraktali.pdf, 17.01.2015. ;