Beruflich Dokumente
Kultur Dokumente
a) softverska
b) hardverska
Slika 6.1.1 Tipovi implementacija OpenGL biblioteke za Windows platformu, preuzeto iz [1] OpenGL metode realizovane su po modelu klijent-server. Aplikacija (klijent) poziva OpenGL metode, a OpenGL (server) ih interpretira i izvrava. Server se moe nalaziti na tom istom raunaru, na kom se nalazi i klijent ili na drugom raunaru. OpenGL iscrtava grafike objekte u baferu kadra (frame buffer) uzimajui u obzir izabrane reime iscrtavanja. Svaki reim moe se menjati nezavisno od drugih. Definisanje primitiva, izbor reima i druge operacije opisuju se pomou komandi u obliku poziva OpenGL metoda. Grafiki objekti se definiu skupom temena (vertex). Temenu se pridruuju podaci (npr. koordinate, boja i dr.) koji se nazivaju atributima. Uproeni proces prezentacije grafike, slika 6.1.2, se sastoji od sledeih koraka: poziva metoda OpenGL API od strane aplikacije (klijenta), skladitenja zadatih metoda komandi u Command baferu, pranjenja Command bafera (ili programski ili automatski), izvravanja komandi, u redosledu njihovog pojavljivanja, primene transformacije i osvetljenja, rasterizacije pretvaranja 3D scene u 2D projekciju, upis rasterizovane scene u bafer kadra grafike kartice (iscrtavanje). Pravi proces ukljuuje jo neke dodatne korake, ali oni nisu neophodni za razumevanje naina rada OpenGL programske biblioteke. itaocima koje to zanima se preporuuje da pogledaju u literaturi [1].
using Tao.OpenGl;
Veoma je bitno naglasiti da ceo skup Tao Framework skup biblioteka predstavlja samo C# wrapper odgovarajuih biblioteka implementiranih korienjem nativnog programskog jezika (konkretno za OpenGL u pitanju je C programski jezik).
Nazivi OpenGL metoda su deskriptivni i obino pruaju informacije o klasi u kojoj su implementirane, broju i tipu parametara. Nomenklatura OpenGL metoda je (sa * su oznaeni opcioni elementi):
tip OpenGL tip podataka koji vraa metoda, <prefiks> karakter koji se eli prikazati, <naziv> - naziv metode, <brojP> - broj parametara dozvoljene vrednosti su: 1, 2, 3, 4. Ovaj
element je opcion i pojavljuje se samo ako su tipovi parametara isti, <tipP> - OpenGL tip podataka. Dozvoljene su sledee vrednosti: b, s, i, f, d, ub, us, ui. Vrednosti predstavljaju skraene oznake OpenGL tipova. Na primer, b odgovara byte tipu podataka, i odgovara int tipu podataka itd. Ovaj element je opcion i pojavljuje se samo ako su tipovi parametara isti, <v> - oznaka da se kao parametar metode koristi pokaziva na niz vrednosti. Ovaj element je opcion i pojavljuje se samo ako su tipovi parametara isti, tip1, ..., tipN OpenGL tipovi podataka parametara 1 .. N, param1, ..., paramN nazivi parametara 1 .. N.
VI Open Graphics Library (OpenGL) C# Primer metode sa oznakama prikazan je na slici 6.1.2.1. Ovakva nomenklatura u znaajnoj meri olakava pam enje naziva metoda, kao i broja i tipa parametara.
glColor3f()
public static void glEnable(int cap) public static void glDisable(int cap)
gde je:
Metoda glIsEnabled se koristi za ispitivanje da li odreena promenljiva stanja ukljuena ili ne.
esto postoji potreba da se stanje sistema sauva/restaurira. OpenGL specifikacija definie FIFO stek stanja sistema, koji omoguava realizaciju gore
VI Open Graphics Library (OpenGL) C# navedenih metoda. Stekom se manipulie korienjem metoda glPushAttrib i glPopAttrib.
Opis
Enum argument je van opsega. Numeriki argument je van opsega. Trenutno stanje sistema ne dozvoljava izvrenje metode. Izvrenje metode izaziva stack overflow. Izvrenje metode izaziva stack underflow. Za izvrenje OpenGL metode sistema nema dovoljno slobodno memorije. Zadata tabela je suvie velika. Nema greke uspeno izvrena OpenGL metoda.
Za utvrivanje koja zastavica je postavljena koristi se metoda glGetError koja vraa vrednost iz tabele 6.1.4.1. Za dobijanje deskriptivnog tekstualnog opisa greke koristi se metoda gluErrorString.
VI Open Graphics Library (OpenGL) C# Ako se desilo vie greaka tada se one mogu dobiti sukcesivnim pozivima metode glGetError sve dok povratna vrednost metode ne bude vrednost: GL_NO_ERROR. S toga je uobiajno da se poziv ove metode nalazi unutar petlje. OpenGL omoguava prezentaciju raunarske grafike nezavisno od konkretnog hardvera, i kao takav se moe posmatrati kao softverski interfejs prema grafikom hardveru. Poto OpenGL vri prezentaciju grafike, on ne sadri u sebi nikakve specijalne komande za rad sa prozorima ili prihvat ulaznih podataka od korisnika. Oslanjanje na neku konkretnu platformu postaje problem ako se eli pisati prenosiv (portable) programski kd, jer tada treba nekako apstrahovati platformske specifinosti rukovanja prozorima, ulazno-izlaznim ureajima i dr. S toga su napravljene prenosive biblioteke koje obezbeuju esto koriene metode za meusobno delovanje raunara i korisnika, kao i za prikaz informacija preko podsistema prozora. Najpopularnija biblioteka je openGL Utility Toolkit (GLUT). GLUT nije u sastavu OpenGL standarda, ali se ukljuuje u sve njegove distribucije i postoje realizacije za razliite platforme. GLUT predstavlja samo minimalni skup metoda za pravljenje OpenGL aplikacija. GLUT je kreirao Mark Kilgard, inenjer SGI, sa ciljem da se omogui programerima da piu u potpunosti prenosive aplikacije. GLUT omoguava rukovanje prozorima, rad sa pop-up menijima, prihvat ulaznih podataka sa tastature, mia i dojstika i dr. Razvoj GLUT programske biblioteke (nije open source) je naputen za Windows platformu. Razvoj je nastavljen kroz open source projekat freeglut. U primerima bie koriena freeglut programska biblioteka, koja se moe preuzeti sa Internet adrese: http://freeglut.sourceforge.net/. Nomenklatura GLUT biblioteke je ista kao i kod drugih OpenGL programskih biblioteka i sve metode imaju prefiks glut, a konstante GLUT_. Da bi se mogla koristiti GLUT programska biblioteka potrebno je: 1. instalirati freeglut biblioteku, 2. podesiti razvojno okruenje za rad sa freeglut bibliotekom, 3. u zagavlje fajl/ova dodati sledei programski kd:
using Tao.FreeGlut;
S obzirom na portabilnost, GLUT programska biblioteka ne moe u potpunosti realizovati sve specifinosti platforme. GLUT programska biblioteka je izabrana da bi se italac fokusirao na OpenGL programiranje raunarske grafike i pisanje prenosivih aplikacija. U daljem tekstu programski kd i detalji e se odnositi na implementaciju pomou C# programskog jezika na Windows platformi.
U daljem tekstu bie prezentovan programski kd aplikacije koja prikazuje prozor plave boje koristei se OpenGL i GLUT programskim bibliotekama. U listingu 5.1.7.1 prikazan je programski kd ove aplikacije.
/*********************************************************************** * Modul: Program.cs * Autor: Srdjan Mihic * Namena: kreiranje jednostavnog prozora oslanjajuci se na OpenGL i GLUT ************************************************************************/ using System; using System.Collections.Generic; using System.Text; // OpenGL i FreeGlut namespaces using Tao.OpenGl; using Tao.FreeGlut; namespace GLUT_prozor { class Program { //////////////////////////////////////////////////////////////////////// // Naziv: RenderScene // Namena: metoda iscrtava OpenGL scenu - plavu pozadinu //////////////////////////////////////////////////////////////////////// static void RenderScene() { // obrisi prozor sa trenutno aktivnom bojom za brisanje ekrana Gl.glClear(Gl.GL_COLOR_BUFFER_BIT); // prekopiraj sadrzaj iz zadnjeg bafera u frame bafer Glut.glutSwapBuffers(); } //////////////////////////////////////////////////////////////////////// // Naziv: SetupRenderingContext // Namena: metoda vrsi potrebne inicijalizacije pre pocetka // GLUT petlje //////////////////////////////////////////////////////////////////////// static void SetupRenderingContext() { // podesi boju za brisanje ekrana // R G B A Gl.glClearColor(0.0f, 0.0f, 1.0f, 1.0f); } ////////////////////////////////////////////////////////////////////// // Naziv: Main // Namena: Main metoda // Parametri: args ////////////////////////////////////////////////////////////////////// static void Main(string[] args) { Glut.glutInit(); // inicijalizuj GLUT // koristi double-buffer i RGBA model boja Glut.glutInitDisplayMode(Glut.GLUT_SINGLE | Glut.GLUT_RGBA); // podesi velicinu prozora Glut.glutInitWindowSize(800, 600); // kreiraj prozor sa naslovom "GLUT prozor"
Listing 6.1.7.1 Jednostavan GLUT prozor plave boje Svaka GLUT aplikacija (main metoda) prolazi kroz sledee faze: inicijalizacija GLUT programske biblioteke, incijalizacija parametara prozora, kreiranje prozora, registrovanje obraivaa dogaaja (callback metode), podeavanja OpenGL konteksta, pokretanje glavne petlje.
Metoda glutInit vri inicijalizaciju GLUT biblioteke, opciono sa proslenim argumentima komandne linije. Poziv ove metode mora prethoditi pozivu bilo koje druge GLUT ili OpenGL metode. U upotrebi je najee verzija metode bez parametara.
public static void glutInit(ref int argcp, StringBuilder[] argv) public static void glutInit()
gde su:
Inicijalizacija prozora se sastoji od podeavanja odgovarajuih bafera kadra, poetnog pozicije i dimenzija prozora. Metoda glutInitDisplayMode definie parametre kadra: model boja i tip baferovanja. Postoje dva tipa baferovanja single i double. Single tip baferovanja direktno iscrtava u bafer kadra, to najee dovodi do treperenja (flickering). Ovaj tip baferovanja se veoma retko koristi. Najee se koristi double baferovanje kod kojeg se iscrtava u zadnji bafer (offscreen buffer). Nakon zavrenog iscrtavanja sadraj zadnjeg bafera se prekopira u bafer kadra. Ovo se realizuje pomou metode glutSwapBuffers. Postoji nekoliko vrsta bafera. Gore navedeni baferi se nazivaju baferima boje (color buffers). Postoji jo i bafer dubine kadra (depth buffer), koji definie rastojanje objekata od korisnika.
argc nemodifikovani argc argument main metode, argv nemodifikovani argv argument main metode.
Metoda glutInitWindowSize se koristi za zadavanje poetnih dimenzija prozora. Poetna pozicija prozora na ekranu se moe zadati pomou glutInitWindowPosition metode. Argumenti u pozivima ovih metoda predstavljaju predlog eljenih vrednosti koje ne moraju nuno biti prihvaene.
mode bitwise OR kombinacija konstanti. Neke od esto korienih vrednosti su: GLUT_RGBA RGBA mod. Ovaj mod je podrazumevani ako se ne navede nijedan drugi. GLUT_RGB - alias za GLUT_RGBA. GLUT_INDEX indeksni mod boja. GLUT_SINGLE single tip baferovanja. Ovaj tip baferovanja je podrazumevani. GLUT_DOUBLE - double tip baferovanja. GLUT_ALPHA mod sa alfa komponentom. GLUT_DEPTH prozor sa baferom dubine.
public static void glutInitWindowSize(int width, int height) public static void glutInitWindowPosition(int x, int y)
gde su:
width irina prozora izraena u pikselima, height - visina prozora izraena u pikselima, x broj piksela od leve ivice ekrana. Podrazumevana vrednost je -1, to
znai da od upravljaa prozora (window manager) zavisi gde e se prozor pojaviti. Izabrana vrednost bi trebalo da bude pozitivan broj ne vei od irine ekrana. broj piksela od gornje ivice ekrana. Podrazumevana vrednost je -1, to znai da od upravljaa prozora (window manager) zavisi gde e se prozor pojaviti. Izabrana vrednost bi trebalo da bude pozitivan broj ne vei od visine ekrana.
Nakon zadavanja parametara prozora potrebno je kreirati prozor pozivom metode glutCreateWindow. Kreiranom prozoru se implicitno pridruuje OpenGL kontekst. Odmah po izvrenju ove metode nad kontekstom se mogu izvravati OpenGL metode. Prozor je skriven sve dok se ne ue u glavnu petlju. Metoda glutCreateWindow vraa identifikator prozora koji je interesantan u sistemima sa vie prozora. GLUT dozvoljava kreiranje i potprozora korienjem metode glutCreateSubWindow.
public static int glutCreateWindow(string name) public static int glutCreateSubWindow(int win, int x, int y,
S. Mihi, D. Dragan, V.Petrovi, D.Iveti RAUNARSKA GRAFIKA VEBE
name naslov prozora koji se prikazuje u naslovnoj liniji (titlebar), win identifikator parent prozora, x x pozicija potprozora, y y pozicija potprozora, width irina potprozora,
GLUT bibliteka omoguava definisanje obraivaa dogaaja preko tzv. metoda sa povratnim pozivom (callback function). Metode sa povrtanim pozivom su realizovane u C# kao delegati (delegates). Kontrola svih dogaaja i pozivanje potrebnih metoda obavlja se unutar glavne (beskonane) petlje. Postoji veliki broj dogaaja na koje se moe reagovati, tako da e biti prezentovani u okviru odgovarajuih poglavlja. Esencijalna metoda je glutDisplayFunc koja registruje metodu za iscrtavanje sadraja prozora. Ta metoda se poziva kad god sadraj prozora treba da se prikae.
public static void glutDisplayFunc( DisplayCallback func) public delegate void Glut.DisplayCallback()
gde je:
Uobiajno je da se podeavanja OpenGL konteksta enkapsuliraju metodom SetupRenderingContext. U primeru iz listinga 6.1.7.1 se u ovoj metodi podeava boja kojom e biti prebrisan sadraj prozora (boja pozadine). Metoda glClearColor definie boju za brisanje prozora. Podrazumevana boja za brisanje je crna boja. S. Mihi, D. Dragan, V.Petrovi, D.Iveti RAUNARSKA GRAFIKA VEBE
koriene vrednosti su: GL_COLOR_BUFFER_BIT nalae ispunu sadraja kolor bafera bojom definisanom sa glClearColor, GL_DEPTH_BUFFER_BIT nalae ienje bafera dubine.
10
11
font naziv fonta, predefinisan, za detalje videti [1, str. 40], character karakter koji se eli prikazati.
font naziv fonta, predefinisan, za detalje videti [1, str. 40], character karakter za koji se eli odrediti irina.
Horizontalan ispis bitmap teksta se realizuje tako to pomeri bitmap kurzor na eljenu poziciju. Zatim se pozivom metode glutBitmapCharacter ispisuje karakter. Ova metoda pomera bitmap kurzor za irinu karaktera. Svaki drugi ispis bitmap teksta zahteva runo pozicioniranje bitmap kurzora.
12
//////////////////////////////////////////////////////////////////////// // Naziv: CalculateBitmapTextWidth // Namena: metoda utvrdjuje sirinu teksta // Parametri: font i tekst // Povratna vrednost: sirina //////////////////////////////////////////////////////////////////////// static int CalculateBitmapTextWidth(IntPtr font, string text) { int width = 0; foreach (char c in text) width += Glut.glutBitmapWidth(font, c); return width; } //////////////////////////////////////////////////////////////////////// // Naziv: RenderBitmapTextH // Namena: metoda iscrtava string horizontalno text // pozivajuci glutBitmapCharacter metodu // Parametri: pozicija (pos_x, pos_y, depth), font i tekst //////////////////////////////////////////////////////////////////////// static void RenderBitmapTextH(float pos_x, float pos_y, float depth, IntPtr font, string text) { Gl.glRasterPos3f(pos_x, pos_y, depth); foreach (char c in text) Glut.glutBitmapCharacter(font, c); }
//////////////////////////////////////////////////////////////////////// // Naziv: RenderBitmapTextV // Namena: metoda iscrtava string vertikalno text pozivajuci // glutBitmapCharacter metodu za svaki karakter // Parametri: pozicija (pos_x, pos_y, depth), font i tekst //////////////////////////////////////////////////////////////////////// static void RenderBitmapTextV(float pos_x, float pos_y, float depth, float fontHeight, IntPtr font, string text) {
13
definisemo orto. projekciju tako da odrzava odnos sirine i visine tj. ako je visina >= od sirine tada koristimo sirinu i izracunamo visinu na osnovu pocetnog odnosa visine/sirine,i obratno (width <= height) // definisi ortogonalnu projekciju halfWindowWidth = halfWidth; Gl.glOrtho(-halfWidth, halfWidth, // na osnovu odnosa sirina/visina -halfWidth/aspectRatio, halfWidth/aspectRatio, 1.0, -1.0);
14
Listing 6.2.2.1 Ispis bitmap teksta GLUT obezbeuje nain da se definie koja metoda treba da bude pozvana kada se promeni veliina prozora, tj. registruje povratni poziv za preraunavanje projekcije. Pored toga, ta metoda se poziva i kod inicijalnog kreiranja prozora. Ako se ne definie ova metoda tada dolazi do deformacije objekata koji se iscrtavaju prilikom izmene dimenzija prozora. GLUT koristi metodu glutReshapeFunc za registrovanje metode koja se poziva prilikom promene dimenzija prozora.
ReshapeCallback func) public delegate void Glut.ReshapeCallback( int width, int height)
gde su:
Metoda ChangeSize definie kako e se skalirati prozor pri promeni veliine prozora od strane korisnika. U ovoj metodi koriste se metode glMatrixMode i glLoadIdentity koje e biti detaljno objanjene u poglavlju 6.5. Uobiajno je da metoda ChangeSize ima sledeu formu: Provera korektnosti novih dimenzija prozora. Ova provera ukljuuje proveru da li je nova vrednost visine prozora jednaka nuli. Ako jeste tada se ova vrednost svodi na najbliu validnu vrednost jedan piksel. Definisanje mapiranja logikih koordinata u fizike koordinate definisanje viewport-a korienjem metode glViewport. Definisanje projekcije. Projekcijom se definie koji deo 3D prostora e biti predstavljen unutar prozora. Postoje dve vrste projekcija: ortogonalna i projekcija u perspektivi. Obe projekcije e biti detaljno opisane kod odgovarajuih OpenGL metoda kojima se definiu. Metoda glViewport definie mapiranje kliping (clipping) prozora u fiziki prozor aplikacije mapiranje logikog koordinatnog sistema u fiziki (slika 6.2.2.1). U primeru 6.2.2.1 kliping prozor se definie da bude sm prozor.
func naziv metode koja e biti pozivana kada prozor promeni veliinu, width i height nove dimenzije prozora.
x, y donja leva koordinata, najee (0,0), width irina kliping prozora., height visina kliping prozora.
16
VI Open Graphics Library (OpenGL) C# Metoda glOrtho definie kliping prozor sa ortogonalnom projekcijom, slika 6.2.2.2. Prostor je definisan preko est parametara dimenzija kvadra. U ortogonalnoj projekciji svi objekti koji su istih dimenzija prikazuju se u istoj veliini bez obzira gde se nalaze. Najee ova projekcija se koristi u CAD i arhitektonskom dizajnu.
public static void glOrtho(double left, double right, double bottom, double top, double near, double far)
gde su:
left, right minimum/maksimum po x-osi, bottom, top minimum/maksimum po y-osi, near, far minimum/maksimum po z-osi.
Slika 6.2.2.2 Ortogonalna projekcija, preuzeto iz [3] Pored glOrtho metode u upotrebi je i metoda gluOrtho2D, koja definie 2D projekciju. Metoda gluOrtho2D predstavlja specijalni sluaj gde su parametar near jednak -1, a parametar far jednak 1.
public static void gluOrtho2D(double left, double right, double bottom, double top)
gde su:
Pomona metoda CalculateBitmapTextWidth izraunava irinu teksta zadatog preko parametra string, koristei se GLUT funkcijom glutBitmapWidth. Pomone metode RenderBitmapTextH i RenderBitmapTextV ispisuju tekst horizontalno, odnosno vertikalno. Ispisani tekst pokazuje primer ravnanja teksta uz levu ivicu, i centralno (gde je potrebno je izraunati irinu teksta i podeliti sa dva). Za pozicioniranje bitmap kurzora koristi se familija metoda kojoj pripada glRasterPos3f metoda, a koja e biti opisana u poglavlju 6.3.1.
17
public static void glutStrokeCharacter(IntPtr font, int character) public static int glutStrokeWidth(IntPtr font, int character)
gde su:
font naziv fonta, predefinisan, za detalje videti [1, str. 42], character karakter koji se eli prikazati/izraunati irina.
Horizontalan ispis outline teksta se realizuje tako to pomeri koord. sistem na eljenu poziciju korienjem metode glTranslatef. Zatim se pozivom metode glutStrokeCharacter ispisuje karakter. Ova metoda pomera koord. sistem za irinu karaktera. Svaki drugi ispis outline teksta zahteva runo pozicioniranje koord. sistema. Transformacije koord. sistema bie detaljno objanjene u poglavlju 6.5.
18
//////////////////////////////////////////////////////////////////////// // Naziv: CalculateStrokeTextWidth // Namena: metoda utvrdjuje sirinu teksta // Parametri: font i tekst // Povratna vrednost: sirina //////////////////////////////////////////////////////////////////////// static int CalculateStrokeTextWidth(IntPtr font, string text) { int width = 0; foreach (char c in text) width += Glut.glutStrokeWidth(font, c); return width; } //////////////////////////////////////////////////////////////////////// // Naziv: RenderStrokeTextH // Namena: metoda iscrtava string horiz. text pozivajuci // glutBitmapCharacter metodu za svaki karakter // Parametri: pozicija (pos_x, pos_y, depth), font i tekst //////////////////////////////////////////////////////////////////////// static void RenderStrokeTextH(float pos_x, float pos_y, float depth, IntPtr font, string text) { Gl.glPushMatrix(); Gl.glTranslatef(pos_x, pos_y, depth); foreach (char c in text) Glut.glutStrokeCharacter(font, c); Gl.glPopMatrix(); } //////////////////////////////////////////////////////////////////////// // Naziv: RenderStrokeTextV // Namena: metoda iscrtava string vertikalno text pozivajuci // glutStrokeCharacter metodu za svaki karakter // Parametri: pozicija (pos_x, pos_y, depth), font i tekst //////////////////////////////////////////////////////////////////////// static void RenderStrokeTextV(float pos_x, float pos_y, float depth, float fontHeight, IntPtr font, string text) {
19
20
Listing 6.2.4.1 Ispis outline teksta Za razliku od prethodnog primera u primeru iz listinga 6.2.4.1 koriena je projekcija u perspektivi, slika 6.2.4.2. U perspektivi objekti koji su dalji od posmatraa se prikazuju manjim i obratno. Ova projekcija omoguava stvaranje realistinog utiska sveta. Metoda gluPerspective definie projekciju u perspektivi.
fovy ugao tj. dubina vidnog polja (field-of-view), aspect odnos irine i visine, zNear, zFar dubina po z-osi.
21
S obzirom da se tekst ispisuje kao vektorska grafika, mogue je definisati debljinu linije korienjem metode glLineWidth, koja je detaljno opisana u poglavlju 6.4.1. Pomona metoda CalculateStrokeTextWidth izraunava irinu teksta zadatog preko parametra string, koristei se GLUT funkcijom glutStrokeWidth. Pomone metode RenderStrokeTextH i RenderStrokeTextV ispisuju tekst horizontalno, odnosno vertikalno na zadatim koordinatama. Ispisani tekst pokazuje primer centriranog teksta (potrebno je izraunati irinu teksta i podeliti sa dva). OpenGL omoguava iscrtavanje sledeih grafikih objekata u prostoru: taaka, linija, trouglova, poligona, punih tela (sfera, kvadra i dr.) i mesh objekata. Ovo poglavlje se sastoji iz etiri sekcije: sekcija o takama i linijama, sekcija o poligonima, sekcije o mesh objektima i sekcije o punim telima. Osnovna jedinica iscrtavanja u OpenGL standardu je taka (vertex). Taka je odreena sa tri koordinate Dekartovog pravouglog koordinatnog sistema (pravilo desne ruke). Taka se definie u OpenGL standardu pozivom neke od metode iz familije metoda glVertex. esto koriene metode iz ove familije su:
void glVertex3f(float x, float y, float z) void glVertex2f(float x, float y) void glVertex3fv(float[] vertex)
gde su:
Da bi se taka, a i bilo koja primitiva, iscrtala na ekranu potrebno je poziv glVertex metode enkapsulirati izmeu para metoda glBegin i glEnd. Ove dve metode definiu tip primitive i enkapsuliraju niz poziva metoda glVertex. Za iscrtavanje taaka mora biti prosleena vrednost Gl.GL_POINTS kao argument glBegin metode. Listing 6.3.1.1 prikazuje programski kod koji iscrtava tri take. S. Mihi, D. Dragan, V.Petrovi, D.Iveti RAUNARSKA GRAFIKA VEBE 22
Gl.glBegin(Gl.GL_POINTS); Gl.glVertex3f(0.0f, 0.0f, 50.0f); Gl.glVertex3f(50.0f, 50.0f, 50.0f); Gl.glVertex3f(-50.0f, 50.0f, 50.0f); Gl.glEnd();
Listing 6.3.1.1 Programski kd za crtanje tri take
Podrazumevana vrednost veliine take iznosi jedan piksel (kvadratnog oblika!). Ako se eli promeniti veliina take tada je potrebno pozvati metodu glPointSize.
Veliina take ne moe biti proizvoljna i zavisi od OpenGL implementacije. Najmanja i najvea podrana veliina take je sadrana u promenljivoj stanja: Gl.GL_POINT_SIZE_RANGE, a korak inkrement u Gl.GL_POINT_SIZE_GRANULITY. Listing 6.3.1.2 prikazuje programski kd za utvrivanje podranih veliina take:
float[] min_max = new float[2]; float[] korak = new float[1]; Gl.glGetFloatv(Gl.GL_POINT_SIZE_RANGE, min_max); Gl.glGetFloatv(Gl.GL_POINT_SIZE_GRANULARITY,
Listing 6.3.1.2 Programski kd za utvrivanje podranih veliina take
korak);
Na taku ne deluje perspektiva tj. taka je iste veliine bez obzira gde je pozicionirana u prostoru. Crtanje linija se realizuje korienjem metode glVertex za definisanje temena linija enkapsulirane glBegin/glEnd metodama sa parametrima Gl.GL_LINES, Gl.GL_LINE_STRIP i Gl.GL_LINE_LOOP. GL_LINES iscrtava niz odvojenih linija definisanih preko svojih temena. S obzirom da je linija definisana sa dve take, ukupan broj taaka mora biti paran. Linija se povlai izmeu dva susedna temena (po redosledu navoenja poziva glVertex metode). Listing 6.3.1.3 prikazuje primer crtanja dve linije.
23
Gl.glBegin(Gl.GL_LINES); Gl.glVertex3f(0.0f, 0.0f, 0.0f); Gl.glVertex3f(50.0f, 50.0f, 50.0f); Gl.glVertex3f(20.0f, 10.0f, 5.0f); Gl.glVertex3f(-50.0f, -50.0f, -50.0f); Gl.glEnd();
GL_LINE_STRIP iscrtava otvorenu poliliniju. Ne postoji ogranienje da broj temena mora biti paran broj, kao kod GL_LINES. Segment polilinije se povlai izmeu dva susedna temena (po redosledu navoenja poziva glVertex metode). Listing 6.3.1.4 prikazuje primer crtanja slova M.
Gl.glBegin(Gl.GL_LINE_STRIP); Gl.glVertex2f(0.0f, 0.0f); Gl.glVertex2f(0.0f, 50.0f); Gl.glVertex2f(15.0f, 0.0f); Gl.glVertex2f(30.0f, 50.0f); Gl.glVertex2f(30.0f, 0.0f); Gl.glEnd();
GL_LINE_LOOP iscrtava zatvorenu poliliniju. Razlika izmeu GL_LINE_STRIP i GL_LINE_LOOP je u tome to za ista temena druga iscrtava i segment koji spaja prvo i poslednje teme. Listing 6.3.1.5 prikazuje primer crtanja trougla (polovine slova M).
24
V Open Graphics Library (OpenGL) Podrazumevana debljina linije iznosi jedan piksel, a moe se promeniti pozivom metode glLineWidth.
float[] min_max = new float[2]; float[] korak = new float[1]; Gl.glGetFloatv(Gl.GL_LINE_WIDTH_RANGE, min_max); Gl.glGetFloatv(Gl.GL_LINE_WIDTH_GRANULARITY,
Listing 6.3.1.6 Programski kd za utvrivanje podranih debljina linije Linija se moe iscrtavati prema ablonu (pattern), kao npr. isprekidana linija. U reim iscrtavanja prema ablonu ulazi se sa: Gl.glEnable (Gl.GL_LINE_STIPPLE). Zatim se poziva metoda glLineStipple, koja je oblika:
korak);
Za izlazak iz reima se koristi metoda glDisable sa argumentom GL_LINE_STIPPLE. Listing 6.3.1.7 prikazuje primer crtanja isprekidane linije korienjem ablona.
// sablon: isprekidana linija ushort sablon = 0x5555; // udji u rezim rada sa sablonima
S. Mihi, D. Dragan, V.Petrovi, D.Iveti RAUNARSKA GRAFIKA VEBE 25
Gl.glEnable(Gl.GL_LINE_STIPPLE); Gl.glLineStipple(1, sablon); // crtanje linija Gl.glBegin(Gl.GL_LINES); Gl.glVertex2f(-80.0f, y); Gl.glVertex2f(80.0f, y); Gl.glEnd(); // izadji iz rezima rada sa sablonima Gl.glDisable(Gl.GL_LINE_STIPPLE);
Pomou linija se moe nacrtati bilo koje grafiki objekat, ali tako nacrtan objekat ne moe biti ispunjen bojom, niti mu se moe pridruiti tekstura. Za grafiki objekat iscrtan pomou linija se kae se da je wireframe objekat. U ovom poglavlju bie prezentovano crtanje poligona: trouglova, etvorouglova i ntouglova. Crtanje trouglova se realizuje korienjem metode glVertex za definisanje temena trouglova enkapsulirane sa glBegin/glEnd metodama sa nekim od parametara: Gl.GL_TRIANGLES, Gl.GL_TRIANGLE_STRIP i Gl.GL_TRIANGLE_FAN. GL_TRIANGLES iscrtava niz odvojenih trouglova definisanih preko svojih temena. S obzirom da je trougao definisan sa tri take, ukupan broj taaka mora biti umnoak broja tri. Trougao odreuju tri susedna temena (po redosledu navoenja poziva glVertex metode). Redosled iscrtavanja linija okvira trougla nije odreen redosledom navoenja temena, ve u smeru suprotnom od smera kretanja kazaljke na satu (counterclockwise, CCW). Listing 6.3.2.1 prikazuje primer crtanja dva odvojena trougla. Listing 6.3.1.7 Primer crtanja isprekidane linije
// def. sablon
Gl.glBegin(Gl.GL_TRIANGLES); Gl.glVertex2f(0.0f, 0.0f); Gl.glVertex2f(0.0f, 50.0f); Gl.glVertex2f(15.0f, 0.0f); Gl.glVertex3f(10.0f, 10.0f, 10.0f); Gl.glVertex3f(25.0f, 50.0f, 5.0f); Gl.glVertex3f(15.0f, 10.0f, -5.0f); Gl.glEnd();
S. Mihi, D. Dragan, V.Petrovi, D.Iveti RAUNARSKA GRAFIKA VEBE
26
V Open Graphics Library (OpenGL) Listing 6.3.2.1 Programski kd za crtanje dva odvojena trougla
GL_TRIANGLE_STRIP iscrtava niz trouglova koji imaju zajednike ivice. Ova primitiva se najee koristi za iscrtavanje mesh objekata. Svaki trougao, osim prvog, u nizu je odreen sa prethodna dva temena u nizu i jednim novim temenom. N trouglova se opisuju preko GL_TRIANGLE_STRIP sa N+2 temena. Listing 6.3.2.2 prikazuje primer crtanja kvadrata pomou dva trougla.
Gl.glEnd();
GL_TRIANGLE_FAN iscrtava niz trouglova koji svi imaju jedno zajedniko teme. Svaki trougao, osim prvog, se definie sa jednim temenom. Listing 6.3.2.3 prikazuje primer crtanja estougla stranica duine deset jedinica (square_root_3 je konstanta koja sadri vrednost kvadratnog korena iz broja tri). Temena se iscrtavaju u smeru kretanja kazaljki na satu (clockwise, CW).
Gl.glBegin(Gl.GL_TRIANGLE_FAN); Gl.glVertex2f(0.0f, 0.0f); Gl.glVertex2f(-10.0f, 0.0f); Gl.glVertex2f(-5.0f, -5.0f * square_root_3); Gl.glVertex2f(5.0f, -5.0f * square_root_3); Gl.glVertex2f(10.0f, 0.0f); Gl.glVertex2f(5.0f, 5.0f * square_root_3); Gl.glVertex2f(-5.0f, 5.0f * square_root_3); Gl.glVertex2f(-10.0f, 0.0f);
Listing 6.3.2.3 Primer crtanja pravilnog estougla pomou trouglova (fan)
Gl.glEnd();
27
VI Open Graphics Library (OpenGL) C# Trouglovi uglavnom predstavljaju najbri nain za definisanje/crtanje objekata, jer je njihovo iscrtavanje hardverski ubrzano (hardware acclerated) kod svakog OpenGL kompatibilnog hardvera. Crtanje etvorouglova se realizuje korienjem metode glVertex za definisanje temena etvorougla enkapsulirane sa glBegin/glEnd metodama sa nekim od parametara: GL_QUADS ili GL_QUAD_STRIP. Ove primitive se iscrtavaju u smeru kretanja kazaljke na satu (CW). Sva temena etvorougla moraju leati u istoj ravni! GL_QUADS crta niz planarnih etvrouglova. etvorougao je definisan sa etiri temena, tako da ukupan broj taaka mora biti umnoak broja etiri. Listing 6.3.2.4 prikazuje primer crtanja kvadrata stranice dimenzija petdeset jedinica.
Gl.glEnd();
GL_QUAD_STRIP crta niz etvorouglova koji imaju zajednike ivice. U mnogome je ekvivalentan GL_TRIANGLE_STRIP primitivi. Za svaki etvorougao, osim prvog, potrebno je definisati dva temena. Listing 6.3.2.5 prikazuje primer crtanja pravougonika pomou dva kvadrata.
Gl.glBegin(Gl.GL_QUAD_STRIP); Gl.glVertex2f(0.0f, 0.0f); Gl.glVertex2f(0.0f, 50.0f); Gl.glVertex2f(50.0f, 0.0f); Gl.glVertex2f(50.0f, 50.0f); Gl.glVertex2f(100.0f, 0.0f); Gl.glVertex2f(100.0f, 50.0f); Gl.glEnd();
Listing 6.3.2.5 Programski kd za crtanje pravougonika preko GL_QUAD_STRIP Crtanje planaranih poligona se realizuje pozivom glBegin sa argumentom Gl.GL_POLYGON i navoenjem temena unutra glBegin/glEnd sekcije. Iscrtavanje poligona je u CCW smeru. Dozvoljeni su samo konveksni poligoni (zbog razliitih S. Mihi, D. Dragan, V.Petrovi, D.Iveti RAUNARSKA GRAFIKA VEBE
28
V Open Graphics Library (OpenGL) optimizacija koje su mogue samo nad konveksim poligonima). Konkavni poligoni se dobijaju spajanjem vie konveksnih poligona. Listing 6.3.2.6 prikazuje primer pravilnog estougla.
Gl.glBegin(Gl.GL_POLYGON); Gl.glVertex2f(-10.0f, 0.0f); Gl.glVertex2f(-5.0f, -5.0f * square_root_3); Gl.glVertex2f(5.0f, -5.0f * square_root_3); Gl.glVertex2f(10.0f, 0.0f); Gl.glVertex2f(5.0f, 5.0f * square_root_3); Gl.glVertex2f(-5.0f, 5.0f * square_root_3);
Listing 6.3.2.6 Primer crtanja pravilnog estougla pomou GL_POLYGON Ispuna poligona moe biti outline ili solid. Outline (wireframe) ispuna poligona crta samo njegove ivice, dok solid dodatno ispunjava poligon zadatom bojom. Za izbor tipa ispune poligona koristi se metoda glPolygonMode, koja ima oblik:
Gl.glEnd();
Ispuna poligona moe biti prema nekom ablonu. U reim ispune poligona ablonom se ulazi pozivom glEnable sa argumentom Gl.GL_POLYGON_STIPPLE. Analogno sa ablonom za linije, ablon za ispunu poligona predstavlja bitmapa (videti poglavlje 5.3.1) dimenzija 32x32 piksela. Za definisanje samog ablona koristi se metoda glPolygonStipple, koja ima oblik:
29
Gl.glEnable(Gl.GL_POLYGON_STIPPLE); // definisi sablon Gl.glPolygonStipple(sablon); // kod koji iscrtava poligon . . . // izadji iz rezima ispune poligona sablonom Gl.glDisable(Gl.GL_POLYGON_STIPPLE);
Prilikom crtanja objekata pomou primitiva prikaz ivica (Gl.GL_LINE mode) unutar objekata uglavnom nije poeljan. OpenGL nudi metodu glEdgeFlag kojom se moe definisati da li su unutranje ivice vidljive. Listing 6.3.2.7 Primer ispune poligona ablonom
Gl.GL_TRUE. Listing 6.3.2.8 prikazuje programski kd koji demonstrira iscrtavanje gore navedenih grafikih primitiva.
/*********************************************************************** * Modul: Program.cs * Autor: Srdjan Mihic * Namena: demonstracija OpenGL primitiva i punih tela ************************************************************************/ using System; using System.Collections.Generic; using System.Text; using Tao.OpenGl; using Tao.FreeGlut; namespace OpenGL_primitive { class Program { enum MenuItem { POINTS = 0, LINES, LINE_STRIP, LINE_LOOP, TRIANGLES, TRIANGLE_STRIP, TRIANGLE_FAN, QUADS, QUAD_STRIP, POLYGON }; // dimenzije prozora static float windowWidth = 300.0f; static float windowHeight = 300.0f; // Globalna promenljiva koja definise koju primitivu nacrtati static MenuItem selectedMenuItem = MenuItem.POINTS;
30
31
----
32
33
Listing 6.3.2.6 Primer crtanja grafikih primitiva Slika 6.3.2.1 prikazuje rezultat rada aplikacije navedenog u listingu 6.3.2.6. Svaka slika rezultat iz aplikacije je uokviren radi lakeg uoavanja.
34
V Open Graphics Library (OpenGL) PREPORUKE ZA KORIENJE: koristiti trouglove umesto etvorouglova i poligona, jer: o uvek su planarni, o uvek su konveksni, voditi rauna da kombinovanje primitiva koje se iscrtavaju u suprotnim smerovima znaajno uspora iscrtavanje.
a) GL_POINTS
b) GL_LINES
c) GL_LINE_STRIP
d) GL_LINE_LOOP
e) GL_TRIANGLES
f) GL_TRIANGLE_STRIP
g) GL_TRIANGLE_FAN h) GL_QUADS i)GL_QUAD_STRIP j) GL_POLYGON Slika 6.3.2.1 Rezultat rada aplikacije iz listinga 6.3.2.8
Za crtanje mesh objekata se najee koriste trouglovi, iz ve navedenih razloga. Svi poligonalni objekti se mogu nacrtati preko trouglova. Kao jednostavan primer bie prezentovano crtanje kupe preko trouglova. Listing 6.3.3.1 i slika 6.3.3.1 prikazuju primer crtanja kupe preko trouglova. Primer je preuzet iz [3].
/*********************************************************************** * Modul: Program.cs * Autor: Richard S. Wright Jr. * Preuzeto: OpenGL SuperBible 4ed * Prilagodio: Srdjan Mihic * Namena: demonstracija kupe sa triangle_strip, depth test i cullinga ************************************************************************/ using System; using System.Collections.Generic;
35
namespace Kupa { class Program { // Rotacija po x i y osi static float xRot = 0.0f; static float yRot = 0.0f; // indikatori izabranih tehnika i mehanizma static bool culling = false; static bool outline = false; static bool depthTesting = false; // stavke menija enum MenuItem { CULLING = 0, OUTLINE, DEPTH_TESTING };
////////////////////////////////////////////////////////////////////// // Naziv: ProcessMenu // Namena: rad sa menijem // Parametri: identifikator odabrane stavke ////////////////////////////////////////////////////////////////////// static void ProcessMenu(int value) { switch ((MenuItem)value) { case MenuItem.DEPTH_TESTING: depthTesting = !depthTesting; break; case MenuItem.CULLING: culling = !culling; break; case MenuItem.OUTLINE: outline = !outline; break; default: break; } Glut.glutPostRedisplay(); } ////////////////////////////////////////////////////////////////////// // Naziv: RenderScene // Namena: metoda iscrtava OpenGL scenu ////////////////////////////////////////////////////////////////////// static void RenderScene() { const float GL_PI = 3.1415f; const float fullCircle = 2.0f * GL_PI; const float step = GL_PI / 8.0f; float x, y, angle; // Koordinate i ugao rotacije
36
37
////////////////////////////////////////////////////////////////////// // Naziv: SetupRenderingContext // Namena: metoda vrsi potrebne inicijalizacije pre pocetka // GLUT petlje ////////////////////////////////////////////////////////////////////// static void SetupRenderingContext() { // Crna pozadina i zelena boja za crtanje Gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f ); Gl.glColor3f(0.0f, 1.0f, 0.0f); // Model sencenja na flat (konstantno) Gl.glShadeModel(Gl.GL_FLAT); // TriangleFan je CW, a podrazumevano je da su front poligoni CCW // zato moramo da obrnemo smer iscrtavanja front poligona
38
////////////////////////////////////////////////////////////////////// // Naziv: ProcessSpecialKeyPress // Namena: metoda obradjuje pritisak na specijalne tastere // Parametri: ASCII kod i pozicija misa ////////////////////////////////////////////////////////////////////// static void ProcessSpecialKeyPress(int key, int x, int y) { switch (key) { case Glut.GLUT_KEY_UP: xRot -= 5.0f; break; case Glut.GLUT_KEY_DOWN: case Glut.GLUT_KEY_LEFT: xRot += 5.0f; break; yRot -= 5.0f; break;
case Glut.GLUT_KEY_RIGHT: yRot += 5.0f; break; }; if if if if (key (key (key (key > < > < 356.0f) -1.0f) 356.0f) -1.0f) xRot xRot yRot yRot = = = = 0.0f; 355.0f; 0.0f; 355.0f;
Glut.glutPostRedisplay(); } ////////////////////////////////////////////////////////////////////// // Naziv: ChangeSize // Namena: metoda obradjuje promenu velicine prozora // Parametri: nove dimenzije prozora ////////////////////////////////////////////////////////////////////// static void ChangeSize(int w, int h) { float nRange = 100.0f; if (h == 0) h = 1; Gl.glViewport(0, 0, w, h); Gl.glMatrixMode(Gl.GL_PROJECTION); Gl.glLoadIdentity(); if (w <= h) Gl.glOrtho(-nRange, nRange, -nRange * h / w, nRange * h / w, -nRange, nRange); else Gl.glOrtho(-nRange * w / h, nRange * w / h, -nRange, nRange, -nRange, nRange); Gl.glMatrixMode(Gl.GL_MODELVIEW); Gl.glLoadIdentity(); } ////////////////////////////////////////////////////////////////////// // Naziv: Main // Namena: Main metoda // Parametri: args // Povratna vrednost: int ////////////////////////////////////////////////////////////////////// static void Main(string[] args) { Glut.glutInit();
39
// Kreiraj meni Glut.glutCreateMenu(ProcessMenu); Glut.glutAddMenuEntry("Toggle depth test", (int)MenuItem.DEPTH_TESTING); Glut.glutAddMenuEntry("Toggle cull backface", (int)MenuItem.CULLING); Glut.glutAddMenuEntry("Toggle outline back", (int)MenuItem.OUTLINE); Glut.glutAttachMenu(Glut.GLUT_RIGHT_BUTTON); Glut.glutReshapeFunc(ChangeSize); Glut.glutSpecialFunc(ProcessSpecialKeyPress); SetupRenderingContext(); Glut.glutMainLoop(); } } }
6.3.3.1 Crtanje kupe kao mesh objekta Gore navedeni primer demonstrira iscrtavanje kupe: aproksimacije kruga kao osnove (pomou Gl.GL_TRIANGLE_FAN) i aproksimacije omotaa (takoe Gl.GL_TRIANGLE_FAN). Susedni trouglovi se iscrtavaju razliitim bojama radi lakeg uoavanja. FRONT poligoni (poligoni lica) su oni poligoni koji su na spoljanjosti objekta. BACK poligoni (poligoni nalija) su poligoni unutranjosti. S toga redosled navoenja temena nije proizvoljan! Ako se temena navode u CW redosledu tada ti poligoni su poligoni lica i obratno (redosled navoenja temena primitiva je prikazan na slici 6.4.3.1). Podrazumevana orijentacija iscrtavanja temena za FRONT poligone je CCW, a za BACK je CW. Poto Gl.GL_TRIANGLE_FAN ima orijentaciju CW, neophodno je izmeniti orijentaciju iscrtavanja FRONT poligona. Ovo se postie pozivom metode glFrontFace sa argumentom Gl.GL_CW.
40
Depth testing omoguava sakrivanje objekata koji su zaklonjeni nekim drugim objektom (sakrivanje po dubini). Ako nije ukljuen, tada je zaklonjen onaj objekat koji je ranije nacrtan (sakrivanje po vremenu iscrtavanja). U primeru se to moe lepo uoiti, ako se kupa rotira oko ose da se vidi i omota i osnova, ako nije ukljuen depth testing osnova e se videti preko omotaa. Back face culling (BFC) (sakrivanje nevidljivih povrina) omoguava znaajnu utedu resursa poto se nevidljive povrine ne iscrtavaju. Utvrivanje koja povrina S. Mihi, D. Dragan, V.Petrovi, D.Iveti RAUNARSKA GRAFIKA VEBE 41
VI Open Graphics Library (OpenGL) C# se iscrtava se odreuje na osnovu orijentacije iscrtavanja temena poligona. Ako je BFC ukljuen tada se ne iscrtavaju BACK poligoni.
c) kupa sa d) kupa sa osnovom sakrivanjem iscrtanom kao nevidljivih povrina iani model
Metoda glutSpecialFunc omoguava definisanje metode koja obrauje ulaz sa tastature i to specijalnih tastera (funkcijski, strelice, home, end, insert, delete, pgup i pgdn). Metoda SpecialKeys definie ugao rotacije kupe na osnovu pritisnutih kurzorskih strelica. U funkciji RenderScene se vri rotaciju kupe na osnovu vrednosti dobijenih iz metode SpecialKeys. Metode glPushMatrix i glRotatef e biti detaljno objanjene u poglavlju 6.5.
Glu.gluDeleteQuadric(object);
42
obj objekat na koji se metoda primenjuje, drawStyle nain iscrtavanja moe biti: Gl.GL_FILL, Gl.GL_LINE,
Gl.GL_POINTS i Gl.GLU_SILHOUETTE. O prva dva stila je bilo rei kod metode glPolygonMode. Gl.GL_POINTS iscrtava objekat samo kao niz temena. Gl.GLU_SILHOUETTE slino kao i Gl.GL_LINE, ali se iscrtavaju samo odreene ivice tako da se vidi samo silueta objekta.
public static void gluQuadricNormals( GLUquadric obj, int normals) public static void gluQuadricOrientation( GLUquadric obj, int orientation)
gde su:
obj quadric objekat za koji se generiu normale, normals tip normala, moe biti:
Glu.GLU_NONE ne generiu se normale, Glu.GLU_FLAT normale su normalne na povrinu (facet), Glu.GLU_SMOOTH normale su tako postavljene da se dobija glatka povrina objekta.
Metoda gluQuadricTexture omoguava automatsko generisanje koordinata teksture. Rad sa teksturama e biti objanjen u poglavlju 6.10.
Glu.GLU_OUTSIDE smer normala je napolje (najee korieno), Glu.GLU_INSIDE normale su usmerene u unutranjost quadric objekta. Koristi se ako eli prikazivati unutranjost objekta, npr. unutranjost kapsule svemirskog broda, koja je modelovana pomou quadric objekta - sfere.
obj quadric objekat za koji se generiu koordinate teksture, textureCoords odreuje da li se generiu koordinate teksture.
Moe biti: Glu.GLU_TRUE generiu se koordinate teksture, Glu.GLU_FALSE ne generiu se koordinate teksture.
43
VI Open Graphics Library (OpenGL) C# Sfera se kreira pomou metode gluSphere. Centar sfere je u koordinatnom poetku i sfera se iscrtava u pozitivnom smeru z ose.
public static void gluSphere(GLUQuadric obj, double radius, int slices, int stacks)
gde su:
obj quadric objekat koji definie parametre iscrtavanja sfere, radius radijus sfere, slices i stacks broj segmenata po x odnosno y osi. (sfera se
Valjak se kreira pomou metode gluCylinder. Centar valjka osnove je u koordinatnom poetku i valjak se iscrtava u pozitivnom smeru z ose. Ako je topRadius jednak nuli tada se dobija kupa.
public static void gluCylinder(GLUquadric obj, double baseRadius, double topRadius, double height, int slices, int stacks)
gde su:
iscrtava aproksimacijom pomou trouglova ili etvorouglova (quad stripes)) Prsten se kreira pomou metode gluDisk. Centar prstena je u koordinatnom poetku i iscrtava se u XY ravni. Ako je innerRadius jednak nuli tada se dobija krug.
obj objekat koji definie parametre iscrtavanja, baseRadius radijus osnove, topRadius radijus vrha, height visina valjka, slices i stacks broj segmenata po x odnosno y osi. (valjak se
public static void gluDisk(GLUquadric obj, double innerRadius, double outerRadius, int slices, int loops)
gde su:
obj objekat koji definie parametre iscrtavanja, innerRadius unutranji radijus, outerRadius spoljanji radijus, slices i loops broj segmenata po x odnosno y osi. (prsten se
44
VI Open Graphics Library (OpenGL) C# GLUT programska biblioteka nudi metode za iscrtavanje geometrijskih tela: sfere, kocke, kupe, torusa, dodekahedrona, oktahedrona, tetrahedrona, ikosahedrona i ajnika. Metode su redom: glutSolidSphere i glutWireSphere, glutSolidCube i glutWireCube, glutSolidCone i glutWireCone, glutSolidTorus i glutWireTorus, glutSolidDodecahedron i glutWireDodecahedron, glutSolidOctahedron i glutWireOctahedron, glutSolidTetrahedron i glutWireTetrahedron, glutSolidIcosahedron i glutWireIcosahedron, glutSolidTeapot i glutWireTeapot. Parametri metoda su slini kao i kod njihovih quadric objekata ekvivalenata. Listing 6.3.4.1 i slika 6.3.4.1 prikazuju primer iscrtavanja quadric i GLUT objekata.
/*********************************************************************** * Modul: Program.cs * Autor: Srdjan Mihic * Namena: demonstracija quadric i GLUT primitiva ************************************************************************/ using System; using System.Collections.Generic; using System.Text; using Tao.OpenGl; using Tao.FreeGlut; namespace GLUT_primitive { class Program { enum MenuItem { GLU_SPHERE = 0, GLU_CYLINDER, GLU_DISK, GLUT_SOLID_SPHERE, GLUT_WIRE_SPHERE, GLUT_TEAPOT }; // dimenzije prozora static float windowWidth = 300.0f; static float windowHeight = 300.0f; // Globalna promenljiva koja definise koju primitivu nacrtati static MenuItem selectedMenuItem = MenuItem.GLU_SPHERE;
////////////////////////////////////////////////////////////////////// // Naziv: ProcessMenu // Namena: rad sa menijem // Parametri: identifikator odabrane stavke ////////////////////////////////////////////////////////////////////// static void ProcessMenu(int value) { selectedMenuItem = (MenuItem)value; // preuzmi izabranu primitivu Glut.glutPostRedisplay(); // refresh } ////////////////////////////////////////////////////////////////////// // Naziv: RenderScene // Namena: metoda iscrtava OpenGL scenu ////////////////////////////////////////////////////////////////////// static void RenderScene() { Gl.glClear(Gl.GL_COLOR_BUFFER_BIT);
45
////////////////////////////////////////////////////////////////////// // Naziv: ChangeSize // Parametri: width i height ////////////////////////////////////////////////////////////////////// static void ChangeSize(int width, int height) { if (height == 0) height = 1; Gl.glViewport(0, 0, width, height); Gl.glMatrixMode(Gl.GL_PROJECTION); Gl.glLoadIdentity();
46
47
a) gluSphere
b) gluCylinder
c) gluDisk
d) glutSolidSphere e) glutWiredSphere
f) glutSolidTeapot
Slika 6.3.4.1 Rezultat rada aplikacije iz listinga 6.3.4.1 GLU biblioteka omoguava kreiranje parametarskih krivih. Metode za njihovo kreiranje bie objaenje u sledeem poglavlju.
public static void glMap1f(int target, float u1, float u2, int stride, int order, float[] points) public static void glMap1f(int target, float u1, float u2, int stride, int order, IntPtr points) public static void glEvalCoord1f(float u)
gde su:
target mora biti vrednost Gl.GL_MAP1_VERTEX_3, u1 i u2 poetna i krajna vrednost, stride razmak izmeu kontrolnih taaka u bajtima, order i points broj i niz kontrolnih taaka.
S. Mihi, D. Dragan, V.Petrovi, D.Iveti RAUNARSKA GRAFIKA VEBE 48
VI Open Graphics Library (OpenGL) C# 3D Bezier-ove zakrpe se realizuju na analogan nain korienjem metoda: glMap2*, glMapGrid2* i glEvalMesh2*, kao i promenljive stanja Gl.GL_MAP2_VERTEX_3. GLU programska biblioteka omoguava crtanje NURBS krivih i zakrpa. Rad je slian kao i kod quadrics objekata. Prvo se kreira NURBS (template) objekat pozivom metode gluNewNurbsRenderer. Zatim se radi nad kreiranim NURBS objektom. Po zavretku rada potrebno je unititi objekat pozivom gluDeleteNurbsRenderer. Parametri NURBS objekta se podeavaju pozivom metode gluNurbsProperty. Ova metoda omoguava podeavanje kvaliteta prikaza objekta, kao i reim iscrtavanje (wireframe ili fill). Programski kod koji crta NURBS krive/zakrpe se enkapsulira pozivom metoda glBeginCurve/glBeginSurface i glEndCurve/glEndSurface. NURBS kriva/zakrpa se definie pozivom metode glNurbsCurve/ glNurbsSurface. Listing 6.3.5.1 i slika 6.3.5.1 prikazuju primer rada sa parametarskim krivama i zakrpama.
/*********************************************************************** * Modul: Programs.cs * Autor: Richard S. Wright Jr. * Prilagodio: Srdjan Mihic * Namena: demonstracija parametarskih krvih i zakrpa ************************************************************************/ using System; using System.Collections.Generic; using System.Text; using Tao.OpenGl; using Tao.FreeGlut; namespace Parametarske_krive_zakrpe { class Program { // Stavke menija enum MenuItem { BEZIER_CURVE = 0, BEZIER_SURFACE, NURBS_CURVE, NURBS_SURFACE }; // Podaci za Bezier krivu static int bcNumPoints = 4; static float[, ] {{ -4.0f, { -6.0f, { 6.0f, { 4.0f, bcCtrlPoints = 0.0f, 0.0f }, 4.0f, 0.0f }, -4.0f, 0.0f }, 0.0f, 0.0f }};
// // // //
// Podaci za Bezier zakrpu static int bsNumPoints = 3; static float[, ,] bsCtrlPoints = {{{ -4.0f, 0.0f, 4.0f }, { -2.0f, 4.0f, 4.0f }, { 4.0f, 0.0f, 4.0f }}, {{ -4.0f, 0.0f, 0.0f },
49
0 v = 1 2 = 3
// //
-2.0f, -6.0f, 0.0f}, -2.0f, -2.0f, 8.0f}, -2.0f, 2.0f, 8.0f}, -2.0f, -2.0f, 0.0f}, -2.0f, 2.0f, 0.0f}, -2.0f, 6.0f, 0.0f}}, 2.0f, -6.0f, 0.0f }, 2.0f, -2.0f, 8.0f }, 2.0f, 2.0f, 8.0f }, 2.0f, -2.0f, 0.0f }, 2.0f, 2.0f, 0.0f }, 2.0f, 6.0f, 0.0f }}, 6.0f, -6.0f, 0.0f}, 6.0f, -2.0f, 0.0f}, 6.0f, 2.0f, 0.0f}, 6.0f, 6.0f, 0.0f}}};
// u = 1, v = 0 // v = 1 // v = 2 // v = 1 // v = 2 // v = 3 // u = 2, // // // // // v = 0 v v v v v = = = = = 1 2 1 2 3
// //
// u = 3, v = 0 // v = 1 // v = 2 // v = 3
// NURBS zakrpa - cvorovi static float[] nsKnots = {0.0f, 0.0f, 0.0f, 0.0f, 1.0f,1.0f,1.0f,1.0f }; // uglovi rotacije static float xRot = 65.0f; static float yRot = 45.0f; // globalna promenljiva koja definise koju parametarsku krivu iscrtati static MenuItem selectedMenuItem = MenuItem.BEZIER_CURVE;
//////////////////////////////////////////////////////////////////////
50
Glut.glutPostRedisplay(); } //////////////////////////////////////////////////////////////////////// // Naziv: ProcessMenu // Namena: metoda obradjuje stavke menija //////////////////////////////////////////////////////////////////////// static void ProcessMenu(int value) { selectedMenuItem = (MenuItem)value; Glut.glutPostRedisplay(); } //////////////////////////////////////////////////////////////////////// // Naziv: RenderScene // Namena: metoda iscrtava OpenGL scenu //////////////////////////////////////////////////////////////////////// static void RenderScene() { Gl.glClear(Gl.GL_COLOR_BUFFER_BIT); Gl.glMatrixMode(Gl.GL_MODELVIEW); Gl.glPushMatrix(); Gl.glRotatef(xRot, 1.0f, 0.0f, 0.0f); Gl.glRotatef(yRot, 0.0f, 1.0f, 0.0f); Gl.glColor3ub(0, 0, 255); switch (selectedMenuItem) { case MenuItem.BEZIER_CURVE: { // Definisi Bezier krivu
51
52
// NURBS objekat // Broj i niz cvorova po U // Broj i niz cvorova po V // Rastojanj po U. // Rastojanje po V
53
// Nacrtaj kontrolne tacke Gl.glPointSize(5.0f); Gl.glColor3ub(255, 0, 0); Gl.glBegin(Gl.GL_POINTS); for (int i = 0; i < nsNumPoints; ++i) for (int j = 0; j < nsNumPoints; ++j) Gl.glVertex3fv(ref nsCtrlPoints[i, j, 0]); Gl.glEnd(); Glu.gluDeleteNurbsRenderer(nurbs); Gl.glDisable(Gl.GL_LIGHT0); Gl.glDisable(Gl.GL_LIGHTING); break; } }; Gl.glPopMatrix(); Glut.glutSwapBuffers(); } //////////////////////////////////////////////////////////////////////// // Naziv: ChangeSize // Namena: metoda obradjuje dogadjaj promene dimenzija prozora // Parametri: nova sirina i visina prozora //////////////////////////////////////////////////////////////////////// static void ChangeSize(int w, int h) { if (h == 0) h = 1; Gl.glViewport(0, 0, w, h); Gl.glMatrixMode(Gl.GL_PROJECTION); Gl.glLoadIdentity(); Gl.glOrtho(-10.0f, 10.0f, -10.0f, 10.0f, -10.0f, 10.0f); Gl.glMatrixMode(Gl.GL_MODELVIEW); Gl.glLoadIdentity(); } //////////////////////////////////////////////////////////////////////// // Naziv: SetupRenderingContext // Namena: metoda vrsi potrebne inicijalizacije pre pocetka // GLUT petlje //////////////////////////////////////////////////////////////////////// static void SetupRenderingContext() { Gl.glEnable(Gl.GL_COLOR_MATERIAL); Gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f); // Automatski generisi normale za evaluirane zakrpe Gl.glEnable(Gl.GL_AUTO_NORMAL); } ////////////////////////////////////////////////////////////////////// // Naziv: Main // Namena: Main metoda // Parametri: args // Povratna vrednost: int ////////////////////////////////////////////////////////////////////// static void Main(string[] args) {
54
Veoma esto postoji potreba za iscrtavanjem vie instanci nekog objekta. Enkapsulacija programskog kda u metode ne daje dovoljno dobre rezultate u aplikacijama gde je bitna performansa. Takoe, esto se geometrija nekog objekta ne menja iz frejma u frejm. OpenGL nudi mehanizam pomou kojeg se moe znaajno ubrzati iscrtavanje sloenih objekata Display List (DL)-e. DL mehanizam omoguava enkapsulaciju i kompilaciju komandi za iscrtavanje objekata u komande hardvera. Ovaj mehanizam se bazira na strukturi OpenGL procesa prezentacije grafike (videti poglavlje 6.1). DL predstavlja niz prekompajliranih komandi na strani servera. Obino DL liste se kreiraju u toku inicijalizacije aplikacije. Definisanje DL listi se realizuje enkapsulacijom programskog kda za iscrtavanje objekata parom glNewList/glEndList komandi. Sledei programski kd prikazuje primer definisanja DL liste.
Gl.glNewList(list_id, Gl.GL_COMPILE);
.
Svaka DL lista se jednoznano identifikuje preko svog naziva koji se navodi kao prvi argument u pozivu glNewList (u primeru je to list_id). Runa definicija S. Mihi, D. Dragan, V.Petrovi, D.Iveti RAUNARSKA GRAFIKA VEBE
Gl.glEndList();
55
VI Open Graphics Library (OpenGL) C# identifikatora liste nije preporuljiva, jer je podlona greci. OpenGL programska biblioteka nudi metodu glGenLists koja generie jedinstvene identifikatore. Metoda glGenLists generie niz uzastopnih identifikatora. Povratna vrednost iz metode je identifikator prvog objekta.
Iscrtavanje DL liste se realizuje korienjem metoda glCallList i glCallLists. Razlika izmeu metoda je u tome to poslednja omoguava izvravanje vie DL lista jednim pozivom.
public static void glCallList(int list_id) public static void glCallLists(int n, int type, IntPtr lists)
gde su:
list_id identifikator DL liste, lists niz identifikatora DL lista, type tip podataka niza lists, obino GL_UNSIGNED_BYTE, n broj identifikatora niza lists.
Ugnjedavanje poziva iscrtavanja DL listi je dozvoljeno, ali ne i ugnjedavanje poziva kreiranja DL lista! Najvie moe biti 64 nivoa hijerarhije. Listing 6.4.6.1 prikazuje iseak programskog koda primera crtanja 1000 kopija aviona korienjem DL mehanizma. . . .
// identifikator aviona static int planeDL;
56
57
Gl.glVertex3f(0.0f, 0.5f, -40.0f); Gl.glVertex3f(3.0f, 0.5f, -57.0f); Gl.glVertex3f(0.0f, 25.0f, -65.0f); Gl.glVertex3f(0.0f, 25.0f, -65.0f); Gl.glVertex3f(-3.0f, 0.5f, -57.0f); Gl.glVertex3f(0.0f, 0.5f, -40.0f); Gl.glVertex3f(3.0f, 0.5f, -57.0f); Gl.glVertex3f(-3.0f, 0.5f, -57.0f); Gl.glVertex3f(0.0f, 25.0f, -65.0f); Gl.glEnd(); // Of Jet } ///////////////////////////////////////////////////////////////////// // Naziv: Shutdown // Namena: oslobadjanje DL listi ///////////////////////////////////////////////////////////////////// static void Shutdown() { Gl.glDeleteLists(planeDL, 1); } /////////////////////////////////////////////////////////////////////// // Naziv: RenderScene // Namena: metoda iscrtava OpenGL scenu /////////////////////////////////////////////////////////////////////// static void RenderScene() { Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT); Gl.glPushMatrix(); Gl.glRotatef(xRot, 1.0f, 0.0f, 0.0f); Gl.glRotatef(yRot, 0.0f, 1.0f, 0.0f); // nacrtaj 1000 aviona for (int i = -5; i < 5; ++i) for (int j = -5; j < 5; ++j) for (int k = -5; k < 5; ++k) { Gl.glPushMatrix(); Gl.glTranslatef(i * 10.0f, k * 10.0f, j * 10.0f); Gl.glScalef(0.1f, 0.1f, 0.1f); // u zavisnosti od izbora crtamo sa ili bez DL if (usingDisplayLists == false) DrawPlane(); else Gl.glCallList(planeDL); Gl.glPopMatrix(); } Gl.glPopMatrix(); Glut.glutSwapBuffers(); }
/////////////////////////////////////////////////////////////////////// // Naziv: SetupRenderingContext // Namena: metoda vrsi inicijalizacije pre pocetka GLUT petlje /////////////////////////////////////////////////////////////////////// static void SetupRenderingContext() { Gl.glEnable(Gl.GL_DEPTH_TEST);
58
2. Signalizacije koji niz temena treba da se iscrta pozivom metode glVertexPointer. Ova metoda signalizira OpenGL gde su smeteni podaci, kao i kog su tipa i koliko ih ima. Za specifikaciju normala i boja koriste se analogne metode: glNormalPointer i glColorPointer i sl.
type
tip koordinate. Dozvoljene vrednosti su: Gl.GL_SHORT, Gl.GL_INT, Gl.GL_FLOAT i Gl.GL_DOUBLE. Inicijalna vrednost je GL_FLOAT,
mode koju primitivu treba iscrtavati tj. kako tumaiti temena, first indeks poetnog temena, count broj temena koja se iscrtavaju.
U indeksnoj verziji postoje dva niza temena: niz indeksa i niz jedinstvenih temena. Ovaj pristup esto obezbeuje bolju performansu. Naime, u velikom broju sluaja kod mesh objekata broj temena se ponavlja jer se poligoni nastavljaju jedan na drugi. S toga umesto prosleivanja niza svih temena od kojih se veliki broj ponavlja, prosleuje se niz jedinstvenih (meusobno razliitih) temena i niz indeksa tih temena. Rad sa indeksnom verzijom vertex array mehanizma se razlikuje od obine verzije u fazi tri gde se niz iscrtava pozivom metode glDrawElements.
60
int
gde su:
mode koju primitivu treba iscrtavati tj. kako tumaiti temena, type tip podataka indeks niza. Dozvoljene vrednosti su:
Gl.GL_UNSIGNED_BYTE, Gl.GL_UNSIGNED_SHORT i Gl.GL_UNSIGNED_INT,
. . .
// da li crtamo sa ili bez koriscenja indeksne verzije vertex arrays bool g_usingIndexedVertexArrays = false; // indeksirana verzija, niz ima elemenata onoliko koliko je temena = 51 static byte[] indices = { 1, 10, 9, 9, 4, 1, 1, 4, 10, 10, 4, 0, 0, 4, 9, 9, 10, 0, 5, 16, 15, 15, 8, 5, 15, 16, 8, 5, 8, 16, 14, 13, 3, 3, 13, 7, 7, 14, 3, 13, 14, 7, 3, 11, 6, 6, 12, 2, 11, 12, 6}; // razlicitih temena ima 17 static float[] uniqueVertices = { 0.0f, 0.0f, -56.0f, 0.0f, 0.0f, 60.0f, 0.0f, 0.5f, -40.0f, 0.0f, -0.5f, -40.0f, 0.0f, 15.0f, 30.0f, 0.0f, 2.0f, 27.0f, 0.0f, 25.0f, -65.0f, 0.0f, 4.0f, -57.0f, 0.0f, 7.0f, -8.0f, 15.0f, 0.0f, 30.0f, -15.0f, 0.0f, 30.0f, 3.0f, 0.5f, -57.0f, -3.0f, 0.5f, -57.0f,
// // // // // // // // // // // // //
0 1 2 3 4 5 6 7 8 9 10 11 12
61
// neindeksirana verzija, 51 teme static float[] vertices = { 0.0f, 0.0f, 60.0f, -15.0f, 0.0f, 30.0f, 15.0f, 0.0f, 30.0f, 15.0f, 0.0f, 30.0f, 0.0f, 15.0f, 30.0f, 0.0f, 0.0f, 60.0f, 0.0f, 0.0f, 60.0f, 0.0f, 15.0f, 30.0f, -15.0f, 0.0f, 30.0f, -15.0f, 0.0f, 30.0f, 0.0f, 15.0f, 30.0f, 0.0f, 0.0f, -56.0f, 0.0f, 0.0f, -56.0f, 0.0f, 15.0f, 30.0f, 15.0f, 0.0f, 30.0f, 15.0f, 0.0f, 30.0f, -15.0f, 0.0f, 30.0f, 0.0f, 0.0f, -56.0f, 0.0f, 2.0f, 27.0f, -60.0f, 2.0f, -8.0f, 60.0f, 2.0f, -8.0f, 60.0f, 2.0f, -8.0f, 0.0f, 7.0f, -8.0f, 0.0f, 2.0f, 27.0f, 60.0f, 2.0f, -8.0f, -60.0f, 2.0f, -8.0f, 0.0f, 7.0f, -8.0f, 0.0f, 2.0f, 27.0f, 0.0f, 7.0f, -8.0f, -60.0f, 2.0f, -8.0f, -30.0f, -0.5f, -57.0f, 30.0f, -0.5f, -57.0f, 0.0f, -0.5f, -40.0f, 0.0f, -0.5f, -40.0f, 30.0f, -0.5f, -57.0f, 0.0f, 4.0f, -57.0f, 0.0f, 4.0f, -57.0f, -30.0f, -0.5f, -57.0f, 0.0f, -0.5f, -40.0f, 30.0f, -0.5f, -57.0f, -30.0f, -0.5f, -57.0f, 0.0f, 4.0f, -57.0f, 0.0f, 0.5f, -40.0f, 3.0f, 0.5f, -57.0f, 0.0f, 25.0f, -65.0f, 0.0f, 25.0f, -65.0f, -3.0f, 0.5f, -57.0f, 0.0f, 0.5f, -40.0f, 3.0f, 0.5f, -57.0f, -3.0f, 0.5f, -57.0f, 0.0f, 25.0f, -65.0f }; //////////////////////////////////////////////////////////////////////// // Naziv: RenderScene // Namena: metoda iscrtava OpenGL scenu //////////////////////////////////////////////////////////////////////// static void RenderScene() { Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT); Gl.glMatrixMode(Gl.GL_MODELVIEW); Gl.glPushMatrix(); Gl.glRotatef(xRot, 1.0f, 0.0f, 0.0f); Gl.glRotatef(yRot, 0.0f, 1.0f, 0.0f); // Ukljuci rad sa vertex array mehanizmom Gl.glEnableClientState(Gl.GL_VERTEX_ARRAY); // Nacrtaj 1000 aviona for (int i = -5; i < 5; ++i) for (int j = -5; j < 5; ++j) for (int k = -5; k < 5; ++k) { Gl.glPushMatrix(); Gl.glTranslatef(i*10.0f, k*10.0f, j*10.0f); Gl.glScalef(0.1f, 0.1f, 0.1f); // U zavisnosti od izbora crtamo sa ili bez DL if (g_usingIndexedVertexArrays == false) { Gl.glVertexPointer(3, Gl.GL_FLOAT, 0, vertices); Gl.glDrawArrays(Gl.GL_TRIANGLES, 0, 51); } else { Gl.glVertexPointer(3, Gl.GL_FLOAT, 0, uniqueVertices); Gl.glDrawElements(Gl.GL_TRIANGLES, 51, Gl.GL_UNSIGNED_BYTE, indices); } Gl.glPopMatrix();
62
Pored VA mehanizma postoji slian mehanizam - Vertex Buffer Object (VBO) koji nudi dodatna podeavanja i fleksibilnost i predstavlja fuziju DL i VA mehanizama. Za razliku od VA mehanizma, VBO mehanizam kreira tzv. bafer objekte (buffer objects) na serverskoj strani (to predstavlja slinost sa DL listama). Metode za pristup i referenciranje podataka u baferima koriste se iste metode kao i kod VA mehanizma. Ovaj mehanizam omoguava kontrolu nad transferom podataka. Podrane su tri mogunosti: GL_STREAM_DRAW, GL_STATIC_DRAW i GL_DYNAMIC_DRAW. GL_STREAM_DRAW reim radi na isti nain kao VA mehanizam podaci se alju prilikom svakog iscrtavanja (pozivi metoda: glDrawArrays, glDrawElements). Ovaj reim je pogodan za iscrtavanje animiranih objekata u sceni. GL_STATIC_DRAW reim alje podatke grafikom kontroleru i oni se skladite u memoriji grafike kartice. Ovaj reim najvie pogoduje za iscrtavanje nedeformabilnih (statikih) objekata. Za razliku od prethodnih u GL_DYNAMIC_DRAW reimu drajveri grafike kartice odreuju gde i kako e biti podaci uskladiteni. Rad sa VBO mehanizmom se realizuje kroz sledee faze: 1. Ukljuenje klijentske promenljive stanja GL_VERTEX_ARRAY pozivom metode glEnableClientState. 2. Kreiranje bafera metodom: glGenBuffers.
. . . Listing 6.4.6.2 Iseak programskog kda za crtanje aviona korienjem Vertex Array mehanizma
public static void glGenBuffers( int size, IntPtr buffers) public static void glGenBuffers( int size, int[] buffers)
gde su:
size broj bafera koji se alocira, buffers niz u koji se smeta niz identifikatora bafera koji se
alociraju. 3. Proglasiti bafer aktivnim pozivom metode glBindBuffer. S. Mihi, D. Dragan, V.Petrovi, D.Iveti RAUNARSKA GRAFIKA VEBE
63
public static void glBindBuffer( int target, int buffer) public static void glBindBuffer( int target, uint buffer)
gde su:
target odreuje na koji tip bafera se vezuje. Mogue vrednosti su: GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, GL_PIXEL_PACK_BUFFER i GL_PIXEL_UNPACK_BUFFER. buffer identifikator aktivnog bafera.
i specifikacija inicijalnih podataka sa
public static void glBufferData( int target, IntPtr size, IntPtr data, int usage) public static void glBufferData( int target, IntPtr size, IntPtr data, int usage)
gde su:
data podaci koji e inicijalno biti kopirani, usage specifikuje reim upotrebe: GL_STREAM_DRAW, GL_STREAM_READ, GL_STREAM_COPY, GL_STATIC_DRAW, GL_STATIC_READ, GL_STATIC_COPY, GL_DYNAMIC_DRAW, GL_DYNAMIC_READ i GL_DYNAMIC_COPY.
5. Deaktiviranje bafera, da ne bi pozivi metoda iscrtavanja sa glBindBuffer gde se umesto podataka alje 0 (zero pointer). 6. Iscrtavanje sadraja bafera se realizuje tako to se bafer proglasi aktivnim, a zatim pozivima glVertexPointer i glDrawArrays na slian nain iscrta. Na primer:
64
// neophodno zbog vertex array-a - iskljucuje // postojeci vertexpointer Gl.glVertexPointer(vertexComponentCount, Gl.GL_FLOAT, 0, IntPtr.Zero); // iscrtaj sadrzaj bafera Gl.glDrawArrays(Gl.GL_TRIANGLES, 0, vertexCount);
7. Na kraju potrebno glDeleteBuffers. je osloboditi bafer pozivom metode
public static void glDeleteBufffers(int int[] buffers) public static void glDeleteBufffers(int IntPtr buffers) public static void glDeleteBufffers(int ref int buffers) public static void glDeleteBufffers(int uint[] buffers) public static void glDeleteBufffers(int ref uint buffers)
gde su:
n, n, n, n, n,
S obzirom da predstavlja kombinaciju najboljih osobina DL i VA mehanizma predstavlja preporuku za korienje. U OpenGL standardu transformacije se realizuju pomou matrica: Viewing, Modeling, Modelview, Projection i Viewport. Redosled primena transformacija je fiksan i identian redosledu navoenja matrica u prethodnoj reenici. S. Mihi, D. Dragan, V.Petrovi, D.Iveti RAUNARSKA GRAFIKA VEBE
65
VI Open Graphics Library (OpenGL) C# Referentni koordinatni sistem je Dekartov pravougli koordinatni sistem, sa pozitivnim pravcem x-ose u desno, pozitivnim pravcem y-ose na gore i pozitivnim pravcem z-ose iz ravni ka korisniku (pravilo desne ruke). b) pomeranje Viewing matrica definie taku a) pomeranje kamere koord. sistema posmatranja kameru (transformie koordinatni sistem). Modeling matrica Slika 6.4.1 Ekvivalencija transformacija omoguava transformacije nad kamere i koord. sistema, preuzeto iz [3] modelom i njegovim objektima. Modelview matrica integrie transformacije koje vre prethodne dve matrice i olakava transformacije nad scenom. Ovo proizilazi iz injenice da sa aspekta posmatraa je svejedno da li se kamera premestila ili su primenjene transformacije nad objektima ako je rezultujui pogled na scenu identian u oba sluaja, slika 6.4.1. Nakon ovih transformacija primenjuju se transformacije projekcije. Projection matrica definie vidljivi volumen, kao i tip projekcije scene na ekran (ortogonalna ili projekcija u perspektivi). Viewport matrica definie mapiranje 2D projekcije scene u prozor u kojem se prikazuje. Najee se manipulacije vri nad Modelview i Projection matricama. Grafika predstava procesa primena transformacija nad temenom je prikazana na slici 6.4.2.
Izbor matrice nad kojom se vre transformacije se realizuje pomou metode glMatrixMode.
66
VI Open Graphics Library (OpenGL) C# Translacija se realizuje pomou familije metoda glTranslate, od kojih je najee koriena metoda glTranslatef.
angle ugao rotacije izraen u stepenima, x, y, z vektor u odnosu na koji se vri rotacija (vektor je definisan sa
Skaliranje se realizuje pomou familije metoda glScale, od kojih je najee koriena metoda glScalef. Uniformno skaliranje se realizuje sa x = y = z. (0,0,0) i (x,y,z)).
// translacija za 10 jedinica po y-osi Gl.glTranslatef(0.0f, 10.0f, 0.0f); // iscrtaj prvu sferu Glut.glutSolidSphere(1.0f, 15, 15); // translacija za 10 jedinica po x-osi Gl.glTranslatef(10.0f, 0.0f, 0.0f); // iscrtaj drugu sferu Glut.glutSolidSphere(1.0f, 15, 15);
Problem nastaje to gore navedeni programski kd zbog kumulativnosti primene transformacija daje rezultat prikazan na slici 6.4.3b.
67
a) eljena pozicija
b) rezultujua pozicija
Slika 6.4.3 Kumulativnost transformacija, preuzeto iz [3] Da bi se izbegao problem kumulativnosti potrebno je imati mogunost resetovanja matrice. U te svrhe se koristi metoda glLoadIdentity. Iako korienje ove metode reava gore pomenute probleme sama metoda je zahtevna. Mnogo bi bilo lake da je mogue nekako sauvati privremeno stanje matrice, primeniti izmene i posle to stanje vrati. OpenGL definie matrini stek (LIFO) za tu namenu i metode glPushMatrix i glPopMatrix. Sve operacije matrinog steka se odnose na trenutno izabranu matricu (korienjem metode glMatrixMode). Sledei programski kd realizuje poziciju sfera kao na slici 6.4.3a:
// sacuvaj stanje Gl.glPushMatrix(); // translacija za 10 jedinica po y-osi Gl.glTranslatef(0.0f, 10.0f, 0.0f); // iscrtaj prvu sferu Glut.glutSolidSphere(1.0f, 15, 15); // vrati staro stanje Gl.glPopMatrix(); // translacija za 10 jedinica po x-osi Gl.glTranslatef(10.0f, 0.0f, 0.0f); // iscrtaj drugu sferu Glut.glutSolidSphere(1.0f, 15, 15);
Listing 6.4.1 i slika 6.4.4 prikazuje iseak programskog kda (RenderScene metodu) simulacije revolucije Zemlje oko Sunca i Meseca oko Zemlje primenom transformacija. Primer je preuzet iz [3]. U primeru se koristi kumulativnost transformacija. Prvo se iscrta Sunce u centru prozora. Zatim se Zemlja zarotira za
Slika 6.4.4 Rezultat rada aplikacije iz listinga 6.4.1 u razliitim vremenskim trenucima
68
fEarthRot ugao u odnosu na Sunce, a onda se Mesec zarotira za fMoonRot ugao u odnosu na Zemlju. Ovo je postignuto upravo korienjem kumulativnosti transformacija.
static void RenderScene() { Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT); // Sacuvaj stanje ModelView matrice i iscrtaj scenu Gl.glMatrixMode(Gl.GL_MODELVIEW); Gl.glPushMatrix(); // Transliraj scenu tako da se vidi unutar prozora Gl.glTranslatef(0.0f, 0.0f, -300.0f); // Sunce Gl.glDisable(Gl.GL_LIGHTING); Gl.glColor3ub(255, 255, 0); Glut.glutSolidSphere(15.0f, 30, 17); Gl.glEnable(Gl.GL_LIGHTING); // Pomeri svetlosni izvor nakon iscrtavanja Sunca Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_POSITION, lightPos); // Rotiraj koordinatni sistem da bi iscrtali Zemlju Gl.glRotatef(earthRotation, 0.0f, 1.0f, 0.0f); // Zemlja Gl.glColor3ub(0, 0, 255); Gl.glTranslatef(105.0f, 0.0f, 0.0f); Glut.glutSolidSphere(15.0f, 30, 17); // Rotiraj za ugao Meseca i nacrtaj Mesec Gl.glColor3ub(200, 200, 200); Gl.glRotatef(moonRotation, 0.0f, 1.0f, 0.0f); Gl.glTranslatef(30.0f, 0.0f, 0.0f); Glut.glutSolidSphere(6.0f, 30, 17); // Restauriraj stanje ModelView matrice pre crtanja Gl.glPopMatrix(); // Modelview matrix // Azuriraj ugao rotacije Zemlje i Meseca (korak je 5 stepeni) moonRotation += 15.0f; if (moonRotation > 360.0f) moonRotation = 0.0f; earthRotation += 5.0f; if (earthRotation > 360.0f) earthRotation = 0.0f; Glut.glutSwapBuffers(); }
OpenGL nudi niz metoda za matrini raun kojima se direktno moe menjati aktivna matrica (npr. ModelView). Vie o ovim metodama se moe nai u [2, 3].
69
U ovom poglavlju bie objanjen rad sa kamerom, kao i mogunosti interakcije sa korisnikom koje prua GLUT programska biblioteka.
public static void gluLookAt( double eyex, double eyey, double eyez, double centerx, double centery, double centerz, double upx, double upy, double upz)
gde su:
eyex, eyey, eyez taka posmatranja, centerx, centery, centerz vektor koji opisuje pravac i upx, upy, upz
smer u kome gleda kamera (forward vector), vektor koji odreuje pravac i smer na gore (upward vector).
Pozicija i orijentacija objekta (obino se koristi termin akter), a i kamere, u prostoru se jednoznano opisuje pomou tri atributa: pozicije objekta, vektora koji definie ta je napred (forward vector) i vektora koji definie ta je iznad (upward vector), slika 6.5.1. Na ovaj nain se znaajno olakavaju transformacije nad objektima, npr. ako treba rotirati avion na slici ulevo tada je potrebno izmeniti njegov Slika 6.5.1 Jednoznano odreivanje forward vector. Dodatno, olakane su i pozicije i orijentacije objekta u prostoru transformacije objekata u odnosu na druge objekte, npr. primer revolucije Zemlje i Meseca oko Sunca Mesec se rotira u odnosu na Zemlju i sa njom oko Sunca. Lokalni koordinatni sistem objekta odreen na ovaj nain se naziva frame of reference. Interakcija sa korisnikom je omoguena GLUT metodama: glutKeyboardFunc, glutKeyboardUpFunc, glutGetModifiers, glutSpecialFunc, glutSpecialUpFunc, glutMouseFunc, glutMotionFunc, glutPassiveMotionFunc, glutEntryFunc i glutIgnoreKeyRepeat.
70
public static void glutKeyboardFunc( KeyboardCallback func) public delegate void Glut.KeyboardCallback( byte key, int x, int y) public static void glutKeyboardUpFunc( KeyboardUpCallback func) public delegate void Glut.KeyboardUpCallback( byte key, int x, int y) public static int glutGetModifiers()
gde su:
key ASCII karakter koji je pritisnut/se otputa, x,y koordinate mia prilikom pritiskanja/otputanja.
Povratna vrednost od glutGetModifiers je broj jedan ili OR kombinacija sledeih vrednosti: Glut.GLUT_ACTIVE_SHIFT, Glut.GLUT_ACTIVE_CTRL i Glut.GLUT_ACTIVE_ALT. Metoda glutSpecialFunc omoguava registrovanje callback metode za obradu pritiska specijalnih karaktera na tastaturi (funkcijski tasteri, Home, Delete, Insert, End, PgUp, PgDn i kurzorske strelice). Metoda glutSpecialUpFunc omoguava registrovanje callback metode za obradu otputanja specijalnog karaktera na tastaturi.
public static void glutSpecialFunc( SpecialCallback func) public delegate void Glut.SpecialCallback( int key, int x, int y) public static void glutSpecialUpFunc( SpecialUoCallback func)
S. Mihi, D. Dragan, V.Petrovi, D.Iveti RAUNARSKA GRAFIKA VEBE
71
GLUT parsira pritisnut taster sve dok se ne otpusti. Ovakvo ponaanje nije uvek pogodno. Npr. ako je pritisnuto vie tastera odjednom, ovakav mehanizam registruje samo pritisak prvog tastera. S toga, metoda glutIgnoreKeyRepeat omoguava iskljuivanje procesiranja dranja pritisnutog tastera.
public static void glutMouseFunc( MouseCallback func) public delegate void Glut.MouseCallback( int button, int state, int x, int y) public static void glutMotionFunc(
S. Mihi, D. Dragan, V.Petrovi, D.Iveti RAUNARSKA GRAFIKA VEBE
72
MotionCallBack func) public delegate void Glut.MotionCallback( int x, int y) public static void glutPassiveMotionFunc( PassiveMotionCallback func) public delegate void Glut.PassiveMotionCallback( int x, int y) public static void glutEntryFunc( EntryCallback func) public delegate void Glut.EntryCallback(int state)
gde su:
U dalje tekstu bie prezentovan primer upravljanja kamerom korienjem W,A,S,D tastera i strelica za pomeranje, kao i pomeranjem mia za rotaciju pogleda. Programski kod primera prikazan je u listingu 6.5.4.1. Rezultat rada aplikacije je prikazan na slici 6.5.4.1. U primeru se koriste i metode glutFullScreen, glutReshapeWindow i glutSetCursor koje prebacuju prikaz preko celog ekrana, menjaju dimenzije prozora i menjaju izgled kurzora, istim redosledom.
/*********************************************************************** * Modul: Program.cs * Autor: Srdjan Mihic * Namena: demonstracija GLUT metoda za interakciju za pomeranje kamere ************************************************************************/ using System; using System.Collections.Generic; using System.Text; using Tao.OpenGl; using Tao.FreeGlut; namespace Kamera { class Program
73
74
75
76
// ne dopusta se rotacija veca od 360/-360 stepeni if (lastRotationZ + angleZ < -1.0f || lastRotationZ + angleZ > 1.0f) return;
77
// zapamti ugao lastRotationZ += angleZ; camera.RotateViewByMouse(angleY, angleZ); Glut.glutPostRedisplay(); } //////////////////////////////////////////////////////////////////////// // Naziv: RenderScene // Namena: metoda iscrtava OpenGL scenu //////////////////////////////////////////////////////////////////////// static void RenderScene() { Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT); Gl.glMatrixMode(Gl.GL_MODELVIEW); Gl.glLoadIdentity(); // podesi kameru if (deltaMove != 0) camera.Move(deltaMove); if (deltaStrafe != 0) camera.Strafe(deltaStrafe); camera.Update(); camera.Look(); // nacrtaj grid DrawGrid(); // nacrtaj neke objekte kao orijentire u prostoru DrawMarkers(); // nacrtaj objekat - korisnika // objekat da bi bio u trecem licu mora biti ispred kamere! if (thirdPersonView == true) { Gl.glTranslatef(g_camera.ViewX, 0.0f, g_camera.ViewZ); Gl.glColor3ub(255, 255, 0); Glut.glutSolidIcosahedron(); } Glut.glutSwapBuffers(); } ////////////////////////////////////////////////////////////////////// // Naziv: ChangeSize // Namena: obradjuje promenu dimenzija prozora ////////////////////////////////////////////////////////////////////// static void ChangeSize(int width, int height) { if (height == 0) height = 1; Gl.glViewport(0, 0, width, height); Gl.glMatrixMode(Gl.GL_PROJECTION); Gl.glLoadIdentity(); Glu.gluPerspective(45.0f,(float)width/(float)height, 1.0f, 150.0f); Gl.glMatrixMode(Gl.GL_MODELVIEW); Gl.glLoadIdentity(); }
//////////////////////////////////////////////////////////////////////// // Naziv: SetupRenderingContext // Namena: metoda vrsi inicijalizacije pre pocetka GLUT petlje ////////////////////////////////////////////////////////////////////////
78
79
Slika 6.5.4.1 Rezultat primera iz listinga 6.5.4.1 U gore navedenom primeru koristi se klasa Camera, koja enkapsulira funkcionalnost kamere. Listinzi 6.5.4.2 i 6.5.4.3. prikazuje programski kd ove klase.
using using using using System; System.Collections.Generic; System.Text; Tao.OpenGl;
namespace Kamera { class Camera { // pozicija kamere private float m_positionX, m_positionY, m_positionZ; // pogled kamere private float m_viewX, m_viewY, m_viewZ; // up vektor private float m_upVectorX, m_upVectorY, m_upVectorZ; // strafe vektor private float m_strafeX, m_strafeY, m_strafeZ; public float ViewX { get { return m_viewX; } } public float ViewY { get { return m_viewY; } } public float ViewZ { get { return m_viewZ; } } public Camera() { } public Camera(float positionX, float positionY, float positionZ, float viewX, float viewY, float viewZ, float upVectorX, float upVectorY, float upVectorZ) { m_positionX = positionX; m_positionY = positionY; m_positionZ = positionZ; m_viewX = viewX; m_viewY = viewY; m_viewZ = viewZ; m_upVectorX = upVectorX; m_upVectorY = upVectorY; m_upVectorZ = upVectorZ; } // pozicioniranje kamere public void Position(
80
81
* * *
m_positionX; m_positionY; m_positionZ; m_upVectorZ) - (viewZ * m_upVectorY)); m_upVectorX) - (viewX * m_upVectorZ)); m_upVectorY) - (viewY * m_upVectorX));
// normalizacija vektora float magnitude =(float)Math.Sqrt(axisX * axisX + axisY * axisY + axisZ * axisZ); m_strafeX = axisX / magnitude; m_strafeY = axisY / magnitude; m_strafeZ = axisZ / magnitude; } public void Look() { Glu.gluLookAt(m_positionX, m_positionY, m_positionZ, m_viewX, m_viewY, m_viewZ, m_upVectorX, m_upVectorY, m_upVectorZ); } } }
82
VI Open Graphics Library (OpenGL) C# Pored ovih metoda GLUT definie i metode za rad sa spejsbolom, dojstikom i tabletom.
public static void glColor3f(float R, float G, float B) public static void glColor4f(float R, float G, float B, float alpha) public static void glColor3ub(byte R,byte G,byte B)
gde su:
Svi objekti koji iscrtavaju posle poziva glColor metode bie iscrtani u boji koju je taj poziv specifikovao. Boja se uvek odnosi na taku teme objekta. Tako da ako su dva temena nekog objekta obojena razliitim bojama, postavlja se pitanje koje e boje biti linija koja ih povezuje, odnosno kojom bojom e biti ispunjen poligon kojem temena pripadaju. Ovo direktno zavisi od tipa senenja. OpenGL podrava dva tipa senenja: flat i smooth. Ako u sceni ne postoji osvetljenje flat tip senenja (homogeno (Lambert) senenje) e liniju izmeu dva temena obojiti bojom drugog temena, dok smooth senenje (Gouraud senenje) e obojiti liniju prelivom boja temena. Slino se realizuje senenje poligona, sa razlikom da u definisanju senenja uestvuje vie temena. Pravila za homogeno senenje se mogu nai u [2]. Slike 6.6.1 i 6.6.2 prikazuju tipove senenja primenjene na poligon, odnosno liniju.
83
Slika 6.6.2 Tipovi senenja na primeru linije Model senenja se definie pomou metode glShadeModel, koja ima oblik:
Listing 6.6.1 prikazuje programski kd aplikacije koja demonstrira tipove senenja na primeru definisanja boje ispune trougla, slika 6.6.1. Primer je preuzet iz [3].
/*********************************************************************** * Modul: Programs.cs * Autor: Richard S. Wright Jr. * Prilagodio: Srdjan Mihic * Namena: demonstracija tipova sencenja ************************************************************************/ using System; using System.Collections.Generic; using System.Text; using Tao.OpenGl; using Tao.FreeGlut; namespace Sencenje { class Program { // stavke menija enum MenuItem {FLAT = 0, SMOOTH};
84
85
6.7 Materijali
U OpenGL standardu materijal se opisuje preko svojih reflektivnih karakteristika. Materijal se opisuje preko tri svetlosne komponente: ambijentalne, difuzne i spekularne. Ambijentalno svetlo osvetljava objekte jednako i kao da je sveprisutno tj. ne dolazi iz nekog konkretnog pravca. Primer ovog osvetljenja je dnevna svetlost. S. Mihi, D. Dragan, V.Petrovi, D.Iveti RAUNARSKA GRAFIKA VEBE
86
VI Open Graphics Library (OpenGL) C# Difuzno svetlo osvetljava objekte iz nekog konkretnog pravca. Svetlosni zraci se prelamaju i rasipaju na povrini objekta. Ova komponenta svetla kreira senke. Primer ovog osvetljenja je stona lampa. Spekularno svetlo (specular highlight), slino kao i difuzno svetlo, dolazi iz nekog pravca, ali refleksija je pod mnogo otrijim uglom i nema rasipanja. Ova svetlost kreira veoma jako osvetljene povrine na objektu odsjaj. Pored ovih osnovnih komponenti moe se definisati i emisiona komponenta. Ona predstavlja boju koju materijal isijava. Materijal se definie pomou familije metoda glMaterial, od kojih se najee koristi:
public static void glMaterialfv(int face,int pname, IntPtr params) public static void glMaterialfv(int face,int pname, float[] params)
gde su:
face tip orijentacije (tip poligona, videti poglavlje 6.4.2) nad kojim se
primenjuje materijal. Dozvoljene vrednosti su: Gl.GL_FRONT, Gl.GL_BACK ili Gl.GL_FRONT_AND_BACK. Poetna vrednost je Gl.GL_FRONT_AND_BACK, pname koja komponenta se podeava sa param parametrom. Mogue vrednosti su: Gl.GL_AMBIENT, Gl.GL_DIFFUSE, Gl.GL_SPECULAR, Gl.GL_EMISSION, Gl.GL_SHININESS, Gl.GL_COLOR_INDEXES, i Gl.GL_AMBIENT_AND_DIFFUSE. Poetna vrednost je Gl.GL_AMBIENT_AND_DIFFUSE, params parametar ija semantika zavisi od pname, uglavnom je boja.
Ovakav nain navoenja materijala za svaki objekat nije uvek pogodan. OpenGL nudi alternativni nain definisanja materijala color tracking. Color tracking podrazumeva definisanje materijala preko poziva glColor metode. U color tracking reim se ulazi pozivom metode glEnable sa argumentom Gl.GL_COLOR_MATERIAL. Funkcijom glColorMaterial se specifikuje na koje orijentacije poligona i koje komponente osvetljenja se boja materijala odnosi (parametri ove metode su isti sa prva dva parametra metode glMaterialfv). U praksi se najee koristi ovakav nain definisanja materijala.
face tip orijentacije (tip poligona, videti poglavlje 6.4.2) nad kojim se
primenjuje materijal. Dozvoljene vrednosti su: Gl.GL_FRONT, Gl.GL_BACK ili Gl.GL_FRONT_AND_BACK. Poetna vrednost je Gl.GL_FRONT_AND_BACK,
87
88
89
90
/*********************************************************************** * Modul: Program.cs * Autor: Richard S. Wright Jr. * Prilagodio: Srdjan Mihic * Namena: demonstracija glMaterial funkcije za zadavanje materijala ************************************************************************/ using System; using System.Collections.Generic; using System.Text; using Tao.OpenGl;
91
92
93
94
Osvetljenje je esencijalno u postizanju realizma modelovane scene. Takoe, svojstva materijala dolaze do izraaja tek kada je u sceni prisutno osvetljenje. U OpenGL standardu se proraun osvetljenja ukljuuje pozivom metode glEnable sa argumentom Gl.GL_LIGHTING. Analogno se iskljuuje proraun osvetljenja.
S. Mihi, D. Dragan, V.Petrovi, D.Iveti RAUNARSKA GRAFIKA VEBE 95
6.8 Osvetljenje
VI Open Graphics Library (OpenGL) C# U proraunu osvetljenja nekog objekat OpenGL ne koristi napredne algoritme kao to je npr. ray-tracing. OpenGL koristi Phong-ov iluminacioni model. Za proraun osvetljenosti nekog poligona potrebno je poznavati njegov vektor normale. Vektor normale se definie za svako teme objekta i mora biti normalizovan. Mogue je definisati samo jedan vektor normale za poligon vektor normale na ravan u kojoj poligon lei (OpenGL dozvoljava samo planarne poligone!). U OpenGL standardu vektor normale se zadaje pomou familije metoda glNormal, od kojih se najee koristi:
public static float[] FindFaceNormal( float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3)
gde su:
nx, ny, nz izraunati vektor normale, x1,y1,z1,x2,y2,z2,x3,y3,z3 tri temena koje odreuju
ravan poligona.
Transformacije sabijanja/rastezanja modela se primenjuju i na njegove vektore normala, tako da je potrebno izvriti normalizaciju nad vektorima normala objekta nakon primene transformacija. OpenGL omoguava automatsku normalizaciju pomou promenljive stanja Gl.GL_NORMALIZE. Meutim, primena automatske normalizacije smanjuje brzinu prorauna osvetljenja zbog raunske sloenosti. Najbolje je odmah zadavati normalizovane vektore normala. Za definisanje modela osvetljenja scene koristi se metoda glLightModelfv. Uglavnom, ova metoda se koristi za zadavanje ambijentalnog svetlosnog izvora.
public static void glLightModelfv(int pname, float[] params) public static void glLightModelfv(int pname, IntPtr params)
gde su:
96
public static void glLightfv(int light, int pname, float[] params) public static void glLightfv(int light, int pname, IntPtr params)
gde su:
Nakon specifikovanja svetlosnog izvora potrebno ga je ukljuiti pozivom metode glEnable sa argumentom Gl.GL_LIGHT<x>, gde je <x> broj svetlosnog izvora. OpenGL specifikacija definie minimalno osam svetlosnih izvora koje implementacija mora podravati. Maksimum svetlosnih izvora koje implementacija podrava je sadran u konstanti Gl.GL_MAX_LIGHTS koja mora biti vea ili jednaka od osam. Na Windows platformi mogue je maksimalno definisati osam svetlosnih izvora. Ako je potrebno na sceni imati vie svetlosnih izvora, tada se svetlosni izvori moraju reciklirati ili se mora simulirati efekti osvetljenja. Svetlosni izvor se pozicionira u sceni pomou metode glLightfv sa pname jednako Gl.GL_POSITION i kao params se navede pozicija svetlosnog izvora. Pozicija svetlosnog izvora je opisana kao ureena etvorka (x,y,z,w) gde su x,y i z koordinate svetlosnog izvora, a parametar w definie da li je u pitanju reflektorski S. Mihi, D. Dragan, V.Petrovi, D.Iveti RAUNARSKA GRAFIKA VEBE
97
VI Open Graphics Library (OpenGL) C# ili direkcioni svetlosni izvor. Vrednost nula odgovara direkcionom, a vrednost jedan reflektorskom svetlosnom izvoru. Ako se ne navede pozicija svetlosnog izvora OpenGL podrazumeva vrednost (0, 0, 1, 0) tj. direkcioni svetlosni izvor u pravcu z-ose. Transformacije nad Modelview matricom se primenjuju i na poziciju svetlosnog izvora. Ako se eli fiksirati pozicija svetlosnog izvora u sceni tada je potrebno poziciju definisati nakon poziva gluLookAt metode i obratno. Sledei programski kd definie i pozicionira direkcioni svetlosni izvor (sa identifikatorom Gl.GL_LIGHT0):
Ostali parametri reflektorskog svetlosnog izvora se definiu korienjem Gl.GL_SPOT_DIRECTION, Gl.GL_SPOT_CUTOFF i Gl.GL_SPOT_EXPONENT vrednosti za definisanje smera, cut-off ugla i koeficijenta opadanja intenziteta o o o o svetlosti. Cut-off ugao je u opsegu [0 , 90 ] ili 180 . Vrednost 180 za ugao je specijalna i oznaava uniformno rasipanje svetlosti takasti svetlosni izvor. Koeficijent opadanja intenziteta svetlosti, u opsegu [0, 128], definie koliko e svetlosni snop biti fokusiran (videti poglavlje 4.8). Podrazumevane vrednosti su: za o smer (0,0,-1) tj. u smeru z-ose; za ugao 180 i za koeficijent opadanja intenziteta vrednost 0. Sledei programski kd prikazuje postupak kreiranja reflektorskog svetlosnog izvora, njegovo pozicioniranje i ukljuivanje:
ambijentalnaKomponenta = { 0.3f, 0.3f, 0.3f, 1.0f }; difuznaKomponenta = { 0.7f, 0.7f, 0.7f, 1.0f }; smer = { 0.0f, 0.0f, -1.0f };
// Pridrui komponente svetlosnom izvoru 0 Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_AMBIENT, ambijentalnaKomponenta); Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_DIFFUSE, difuznaKomponenta); // Podesi parametre reflektorkskog izvora Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_SPOT_DIRECTION, smer); Gl.glLightf(Gl.GL_LIGHT0, Gl.GL_SPOT_CUTOFF, 45.0f); // Ukljuci svetlosni izvor Gl.glEnable(Gl.GL_LIGHT0); // Pozicioniraj svetlosni izvor float[] pozicija = { -50.f, 50.0f, 100.0f, 1.0f }; Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_POSITION, pozicija);
float[]
98
float[]
// Pridrui komponente svetlosnom izvoru 0 Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_AMBIENT, ambijentalnaKomponenta); Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_DIFFUSE, difuznaKomponenta); // Podesi parametre tackastog svetlosnog izvora Gl.glLightf(Gl.GL_LIGHT0, Gl.GL_SPOT_CUTOFF, 180.0f); // Ukljuci svetlosni izvor Gl.glEnable(Gl.GL_LIGHT0); // Pozicioniraj svetlosni izvor float[] pozicija = { -50.f, 50.0f, 100.0f, 1.0f }; Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_POSITION, pozicija);
Listing 6.8.1. prikazuje programski kd primera primene materijala i osvetljenja (direkciono i ambijentalno) na model aviona, preuzet iz [3]. Avion se moe rotirati korienjem strelica na tastaturi da bi se bolje video uticaj osvetljenja. Slika 6.8.1 prikazuje rezultat aplikacije.
/*********************************************************************** * Modul: Program.cs * Autor: Richard S. Wright Jr. * Prilagodio: Srdjan Mihic * Namena: demonstracija OpenGL osvetljenja ************************************************************************/ using System; using System.Collections.Generic; using System.Text; using Tao.OpenGl; using Tao.FreeGlut;
namespace Osvetljenje { class Program { // Uglovi rotacije static float xRot = 0.0f; static float yRot = 0.0f; //////////////////////////////////////////////////////////////////////// // Naziv: FindFaceNormal // Namena: metoda izracunava normalu za poligon // Parametri: 3 temena // Povratna vrednost:izracunata normala //////////////////////////////////////////////////////////////////////// public static float[] FindFaceNormal(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3) {
99
// normalizacija // duzina vektora normale float len = (float)(Math.Sqrt((normal[0] * normal[0]) + (normal[1] * normal[1]) + (normal[2] * normal[2]))); // izbegava se deljenje sa nulom if (len == 0.0f) len = 1.0f; // normalizacija komponenata normal[0] /= len; normal[1] /= len; normal[2] /= len; return normal; } ///////////////////////////////////////////////////////////////////// // Naziv: ProcessSpecialKeyPress // Namena: metoda procesira strelice // Parametri: key, x, y ////////////////////////////////////////////////////////////////////// static void ProcessSpecialKeyPress(int key, int x, int y) { switch (key) { case Glut.GLUT_KEY_UP: xRot-= 5.0f; break; case Glut.GLUT_KEY_DOWN: xRot += 5.0f; break; case Glut.GLUT_KEY_LEFT: yRot -= 5.0f; break; case Glut.GLUT_KEY_RIGHT: yRot += 5.0f; break; }; Glut.glutPostRedisplay(); } //////////////////////////////////////////////////////////////////////// // Naziv: RenderScene // Namena: metoda iscrtava OpenGL scenu //////////////////////////////////////////////////////////////////////// static void RenderScene() { Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT); // Save matrix state and do the rotation Gl.glPushMatrix(); Gl.glTranslatef(0.0f, 0.0f, -150.0f); Gl.glRotatef(yRot, 0.0f, 1.0f, 0.0f); Gl.glRotatef(xRot, 1.0f, 0.0f, 0.0f); Gl.glBegin(Gl.GL_TRIANGLES); Gl.glNormal3fv(FindFaceNormal(15.0f, 0.0f, 30.0f, 0.0f, 15.0f, 30.0f, 0.0f, 0.0f, 60.0f)); Gl.glVertex3f(15.0f, 0.0f, 30.0f); Gl.glVertex3f(0.0f, 15.0f, 30.0f); Gl.glVertex3f(0.0f, 0.0f, 60.0f);
100
101
102
} }
Listing 6.8.1 Primer rada sa osvetljenjem na primeru aviona U stvarnosti objekti koji su osvetljeni bacaju senku. OpenGL model osvetljenja ne podrava automatsko generisanje senki, tako da autor mora sam
103
VI Open Graphics Library (OpenGL) C# generisati i prikazati senku. Proraun i prikaz senki je van okvira ovog teksta. O senkama itaoci se mogu vie informisati u [3]. Listing 6.8.2. prikazuje programski kd primera korienja reflektorskog osvetljenja, preuzet iz [3]. Svetlosni izvor se moe pomerati korienjem strelica na tastaturi da bi se bolje video uticaj osvetljenja. Slika 6.8.2 prikazuje rezultat aplikacije.
/*********************************************************************** * Modul: Program.cs * Autor: Richard S. Wright Jr. * Prilagodio: Srdjan Mihic * Namena: demonstracija svetlosnih izvora ************************************************************************/ using System; using System.Collections.Generic; using System.Text; using Tao.OpenGl; using Tao.FreeGlut; namespace Izvori { class Program { // Uglovi rotacije static float xRot = 0.0f; static float yRot = 0.0f; // Vrednosti svetlosnih komponenti static float[] dirPos = { 0.0f, 0.0f, 75.0f, 0.0f }; static float[] specular = { 1.0f, 1.0f, 1.0f, 1.0f}; static float[] specref = { 1.0f, 1.0f, 1.0f, 1.0f }; static float[] ambientLight = { 0.5f, 0.5f, 0.5f, 1.0f}; static float[] spotDir = { 0.0f, 0.0f, -1.0f }; static float[] spotPos = { 0.0f, 0.0f, 75.0f, 1.0f }; // Stepeni kvaliteta iscrtavanja enum MenuItem { MODE_FLAT = 0, MODE_SMOOTH, MODE_VERYLOW, MODE_MEDIUM, MODE_VERYHIGH, POINT_SOURCE, SPOT_SOURCE, DIRECT_SOURCE }; static MenuItem shadeMode = MenuItem.MODE_FLAT; static MenuItem tessellation = MenuItem.MODE_VERYLOW; static MenuItem lightSource = MenuItem.DIRECT_SOURCE;
//////////////////////////////////////////////////////////////////// // Naziv: ProcessMenu // Namena: rad sa menijem // Parametri: identifikator izabrane stavke //////////////////////////////////////////////////////////////////// static void ProcessMenu(int value) {
104
105
106
107
Listing 6.8.2 Primer kreiranja svetlosnih izvora OpenGL podrava definisanje efekta magle. Proraun efekta magle se ukljuuje pozivom metode glEnable sa argumentom Gl.GL_FOG. Familija metoda glFog odreuje parametre magle.
public static void glFogi(int pname, int param) public static void glFogf(int pname, float param) public static void glFogfv(int pname,float[] param)
S. Mihi, D. Dragan, V.Petrovi, D.Iveti RAUNARSKA GRAFIKA VEBE
108
Listing 6.8.3 prikazuje iseak programskog kda primera primene efekta magle. Slika 6.8.3 prikazuje rezultat ovog primera. Primer predstavlja proirenje primera upravljanja kamerom (videti poglavlje 6.6.4).
od moguih vrednosti su: Gl.GL_FOG_MODE params je blending faktor (jednaina po kojoj se rauna magla) koji moe biti: Gl.GL_LINEAR, Gl.GL_EXP, and Gl.GL_EXP2. Gl.GL_FOG_DENSITY params je gustina magle, vrednost mora biti nenegativna; inicijalno iznosi 1. Gl.GL_FOG_START params je distanca od koje se poinje primenjivati magla; inicijalno iznosi 0. Gl.GL_FOG_END params je distanca nakon koje svi objekti su u magli; inicijalno iznosi 1. Gl.GL_FOG_COLOR params definie boju magle; inicijalno je (0, 0, 0, 0) crna boja. param semantiku odreuje parametar pname.
109
. . .
////////////////////////////////////////////////////////////////// // Naziv: RenderScene // Namena: metoda iscrtava OpenGL scenu ////////////////////////////////////////////////////////////////// static void RenderScene() { // obrisi prozor sa trenutno aktivnom bojom za brisanje ekrana Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT); Gl.glMatrixMode(Gl.GL_MODELVIEW); Gl.glLoadIdentity(); // podesi kameru if (deltaMove != 0) camera.Move(deltaMove); if (deltaStrafe != 0) camera.Strafe(deltaStrafe); camera.Update(); camera.Look(); // Pozicioniraj svetlosni izvor pre transformacija Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_POSITION, g_lightPosition); // nacrtaj podlogu Gl.glColor3f(0.60f, 0.40f, 0.10f); DrawGround(); // nacrtaj neke objekte kao orijentire u prostoru DrawMarkers(); Glut.glutSwapBuffers(); } //////////////////////////////////////////////////////////////////// // Naziv: SetupRenderingContext // Namena: metoda vrsi potrebne inicijalizacije pre // pocetka GLUT petlje //////////////////////////////////////////////////////////////////// static void SetupRenderingContext() { Gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); camera.Position(0.0f, 1.5f, 6.0f, // eye 0.0f, 1.5f, 0.0f, // forward 0.0f, 1.0f, 0.0f); // up // Siva pozadina Gl.glClearColor(g_grayLight[0], g_grayLight[1], g_grayLight[2], g_grayLight[3]); // Kreiranje efekta magle Gl.glEnable(Gl.GL_FOG); // ukljuci efekat magle // boja magle = boja pozadine Gl.glFogfv(Gl.GL_FOG_COLOR, g_grayLight); // magla pocinje na udaljenosti od 5 jedinica Gl.glFogf(Gl.GL_FOG_START, 5.0f);
110
Pridruivanje teksture objektima doprinosi realizmu scene. U ovom poglavlju bie prezentovane metode za uitavanje tekstura i njihovo mapiranje. Jedinica teksture se naziva teksel. OpenGL podrava rad sa 1D, 2D i 3D teksturama, koje se uitavaju pomou metoda glTexImage1D, glTexImage2D i glTexImage3D.
6.9 Teksture
public static void glTexImage1D(int target, int level, int internalformat, int width, int border, int format, int type, IntPtr data) public static void glTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, IntPtr data) public static void glTexImage3D(int target, int level, int internalformat, int width, int height, int depth, int border, int format, int type, IntPtr data)
gde su:
Teksture se mogu kreirati na osnovu sadraja kolor bafera (iscrtanog dela kadra) pomou glCopyTexImage1D i glCopyTexImage2D metoda.
public static void glCopyTexImage1D(int target, int level, int internalFormat, int x, int y, int width, int border)
S. Mihi, D. Dragan, V.Petrovi, D.Iveti RAUNARSKA GRAFIKA VEBE
112
public static void glCopyTexImage2D(int target, int level, int internalFormat, int x, int y, int width, int height, int border)
gde su:
target odgovarajua vrednost shodno dimenzionalnosti teksture: Gl.GL_TEXTURE_1D i Gl.GL_TEXTURE_2D, level mipmaping level (ako se ne koristi stavlja se 0), internalFormat format teksela, x i y koordinate donje-levog temena regiona koji se kopira iz kolor
bafera,
esto se teksture menjaju u toku izvravanja, uitavaju nove i uklanjaju stare iz memorije. Gore navedene operacije za uitavanje teksture su zahtevne po pitanju resursa. S toga postoje metode glTexSubImage1D, glTexSubImage2D i glTexSubImage3D koje omoguavaju zamenu sadraja teksture.
public static void glTexSubImage1D(int target, int level, int xOffset, int width, int format, int type, IntPtr data) public static void glTexSubImage2D(int target, int level, int xOffset, int yOffset, int width, int height, int format, int type, IntPtr data) public static void glTexSubImage3D(int target, int level, int xOffset, int yOffset, int zOffset, int width, int height, int depth, int format, int type, IntPtr data)
gde su:
target odgovarajua vrednost shodno dimenzionalnosti teksture: Gl.GL_TEXTURE_1D, Gl.GL_TEXTURE_2D i Gl.GL_TEXTURE_3D, level mipmaping level (ako se ne koristi stavlja se 0), xOffset, zOffset i yOffset pozicija u staroj teksturi od koje se
vri zamena,
width, height i depth dimenzije teksture, format, type i data su format, tip piksela i pikseli pikselmape
koja se koristi kao tekstura.
113
VI Open Graphics Library (OpenGL) C# Mogue je zameniti teksturu delom iscrtanog kadra u kolor baferu sa metodama glCopyTexSubImage1D i glCopyTexSubImage2D. Za mapiranje teksture na objekat (tj. teksela na temena objekta) koriste se koordinate teksture. Slino X3D standardu (videti poglavlje 4.7), koordinate teksture se izraavaju u (s,t,r) komponentama koje uzimaju vrednost iz opsega [0,1]. 1D teksture imaju samo s komponentu, 2D komponente s i t, a 3D teksture imaju s,t i r komponente. U reim mapiranja dvodimenzionalne teksture se ulazi pozivom metode glEnable sa argumentom Gl.GL_TEXTURE_2D. Analogno se ulazi u reime mapiranja za teksture drugih dimenzionalnosti. Koordinate 1D, 2D i 3D teksture se specifikuju pomou metoda glTexCoord1f, glTexCoord2f i glTexCoord3f.
public static void glTexCoord1f(float s) public static void glTexCoord2f(float s, float t) public static void glTexCoord3f(float s, float t, float r)
gde su: Za objekte relativno sloene geometrije runo zadavanje koordinata teksture je veoma teko i podlono grekama. OpenGL standard nudi mogunost automatskog generisanja koordinata teksture. Automatsko generisanje koordinata tekstura se ukljuuje preko promenljivih stanja: Gl.GL_TEXTURE_GEN_S, Gl.GL_TEXTURE_GEN_T i Gl.GL_TEXTURE_GEN_R. Za dvodimenzionalne teksture se koriste vrednosti: Gl.GL_TEXTURE_GEN_S i Gl.GL_TEXTURE_GEN_T. Po ukljuivanju sve pozivi glTexCoord* metoda se ignoriu i OpenGL automatski generie koordinate teksture. OpenGL implementaciji se mora zadati na koji nain da generie koordinate teksture. Ovo se postie pozivom familije metoda glTexGen*.
public static void glTexGenf(int coord, int pname, float param) public static void glTexGenfv(int coord, int pname, float[] param) public static void glTexGenfv(int coord, int pname, IntPtr param)
gde su:
coord specifikuje koju koordinatu zadajemo. Dozvoljene vrednosti su: pname vrednost odreuje znaenje param
parametra. Mogue vrednosti su: Gl.GL_OBJECT_PLANE i Gl.GL_EYE_PLANE, i Gl.GL_TEXTURE_GEN_MODE, 114
Gl.GL_S, Gl.GL_T, i Gl.GL_R,
public static void glTexEnvi(int target, int pname, int param) public static void glTexEnvf(int target, int pname, float param) public static void glTexEnviv(int target,int pname, int[] param) public static void glTexEnvfv(int target,int pname, float[] param)
gde su:
target parametar mora biti Gl.GL_TEXTURE_ENV. pname najea vrednost je Gl.GL_TEXTURE_ENV_MODE, tada
params moe biti: Gl.GL_MODULATE mnoi teksel sa bojom materijala, Gl.GL_DECAL blending na osnovu alpha komponente, Gl.GL_BLEND blending teksela sa bojom materijala, Gl.GL_REPLACE teksel zamenjuje boju materijala. params semantiku odreuje parametar pname.
Zbog naina na koji se vri blending teksture, kao rezultat moe doi do gubljenja odsjaja materijala (specular highlight). Ako se eli sauvati odsjaj mora se koristiti secondary specular color tehnika, dostupna od verzije 1.4. U reim rada sa ovom tehnikom se ulazi sledeim programskim kodom:
Gl.glLightModeli(Gl.GL_LIGHT_MODEL_COLOR_CONTROL,
S. Mihi, D. Dragan, V.Petrovi, D.Iveti RAUNARSKA GRAFIKA VEBE
115
Gl.GL_SEPARATE_SPECULAR_COLOR);
Pored reimskog rada mogue je i runo podeavati boju odsjaja. Mapiranje teksela na objekat esto podrazumeva skaliranje teksela. OpenGL omoguava specifikovanje filtara za skaliranje tekstura. Filtri se definiu za smanjenje (minimization) i uveavanje (magnification) teksture. Neki od filtera su: najblii sused (nearest neighbour), slika 6.9.1a, i linearno filtriranje (linear filtering), slika 6.9.1b. Najblii sused odreuje vrednost nedostajueg teksela na osnovu njegovog najblieg suseda, dok linearno filtriranje odreuje vrednost nedostajueg teksela na osnovu srednje vrednosti okolnih teksela. Najblii sused daje vizeulno loiji rezultat u poreenju sa linearnim filtriranjem, ali zato je znaajno bri od linearnog filtriranja. OpenGL zahteva da se definie kako se rukuje koordinatama teksture koje su van opsega ([0,1]) (texture wrapping). Jedna od mogunosti je ponavljanje teksture u pravcu u kome su koordinate teksture izvan opsega.
Gl.glLightModeli(Gl.GL_LIGHT_MODEL_COLOR_CONTROL, Gl.GL_COLOR_SINGLE);
a) najblii sused
b) linearno filtriranje
Slika 6.9.1 Tipovi filtriranja teksture, preuzeto iz [3] Familija metoda glTextParameter omoguava gore navedenu funkcionalnost.
public static void glTexParameterf(int target, int pname, float param) public static void glTexParameteri(int target, int pname, int param) public static void glTexParameterfv(int target, int pname, float[] params) public static void glTexParameterfv(int target,
S. Mihi, D. Dragan, V.Petrovi, D.Iveti RAUNARSKA GRAFIKA VEBE 116
int pname, IntPtr params) public static void glTexParameteriv(int target, int pname, int[] params) public static void glTexParameteriv(int target, int pname, IntPtr params)
gde su: parametar mora biti: Gl.GL_TEXTURE_1D, Gl.GL_TEXTURE_2D ili Gl.GL_TEXTURE_3D. pname moe biti: Gl.GL_TEXTURE_MIN_FILTER filtriranje teksture koje se primenjuje u sluaju da se tekstura mora smanjiti da bi bila pridruena objektu, Gl.GL_TEXTURE_MAG_FILTER filtriranje teksture koje se primenjuje u sluaju da se tekstura mora poveati da bi bila pridruena objektu, Gl.GL_TEXTURE_WRAP_S definie da li e se tekstura ponavljati po s-osi, kao i kojim tekselima e biti realizovano ponavljanje, Gl.GL_TEXTURE_WRAP_T definie da li e se tekstura ponavljati po t-osi, kao i kojim tekselima e biti realizovano ponavljanje, Gl.GL_TEXTURE_BORDER_COLOR definie boju okvira teksture. Podrazumevana vrednost je crna boja, Gl.GL_TEXTURE_PRIORITY definie prioritet teksture. params semantiku odreuje parametar pname. Listing 6.9.1 i slika 6.9.2 prikazuju primer pridruivanja teksture objektu piramidi. Primer je preuzet iz [3].
. . . //////////////////////////////////////////////////////////////////////// // Naziv: RenderScene // Namena: metoda iscrtava OpenGL scenu //////////////////////////////////////////////////////////////////////// static void RenderScene() { float[] top = { 0.0f, .80f, 0.0f }; // Top 0 float[] backLeft = { -0.5f, 0.0f, -.50f }; // Back left 1 float[] backRight = { 0.5f, 0.0f, -0.50f }; // Back right 2 float[] frontRight = { 0.5f, 0.0f, 0.5f }; // Front right 3 float[] frontLeft = { -0.5f, 0.0f, 0.5f }; // Front left 4 Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);
target
117
118
119
Listing 6.9.1 Primer rada sa teksturama - pridruivanje teksture piramidi Veliki problem predstavlja injenica to je tekstura fiksnih dimenzija i kao takva teko da se moe mapirati na objekat bez potrebe za skaliranjem. Skaliranje skoro uvek daje neeljene artefakte. tzv. scintillation artefacts. Ovi artefakti se najbolje uoavaju kod objekata u pokretu. Takoe, skaliranje predstavlja zahtevnu operaciju sa aspekta zauzea resursa. Kao reenje za gore navedene probleme koristi se mipmapping tehnika pridruivanja teksture objektima. Ova tehnika poboljava kako Slika 6.9.2 Rezultat primera iz performanse iscrtavanja tako i vizuelni kvalitet listinga 6.9.1 iscrtanih objekata. Mipmapping koristi umesto jedne teksture, itav niz tekstura (mipmapping levels) iste slike, ali razliitog kvaliteta i dimenzija. OpenGL tada koristi najpogodniju teksturu. Mipmapping tekstura se sastoji od niza slika, gde je svaka slika dva puta manjih dimenzija od prethodne, slika 6.9.3.
. . .
Slika 6.9.3 Mipmapping tekstura Za kreiranje mipmapping tekstura koriste se metode: gluBuild1DMipmaps, gluBuild2DMipmaps i gluBuild3DMipmaps.
public static int gluBuild1DMipmaps(int target, int internalFormat, int width, int format, int type, IntPtr data)
120
public static int gluBuild2DMipmaps(int target, int internalFormat, int width, int height, int format, int type, IntPtr data) public static int gluBuild3DMipmaps(int target, int internalFormat, int width, int height, int depth, int format, int type, IntPtr data)
gde su:
S obzirom na estu upotrebu velikog broja tekstura postoji glGenTextures metoda koja kreira N tekstura odjednom. Ovako kreirana tekstura (se naziva Texture Object u OpenGL terminologiji) se pridruuje objektu pozivom metode glBindTexture. Teksture se unitavaju pozivom metode glDeleteTextures.
public static void glGenTextures(int n, int[] textures) public static void glGenTextures(int n, IntPtr textures)
gde su:
n broj tekstura koje treba da se generiu, textures niz u kom se smetaju identifikatori alociranih tekstura. public static void glBindTexture(int target, int texture)
gde su: S. Mihi, D. Dragan, V.Petrovi, D.Iveti RAUNARSKA GRAFIKA VEBE
121
texture identifikator alocirane teksture koja eli koristiti. public static void glDeleteTextures(int n, int[] textures) public static void glDeleteTextures(int n, IntPtr textures)
gde su:
Slika 6.9.4 Rezultat primera iz listinga 6.9.2 Listing 6.9.2 i slika 6.9.4 prikazuju primer korienja gore navedenih metoda. Primer pokazuje primenu mipmapping tekstura, kao i primenu razliitih filtara nad teksturama. Primer je preuzet iz [3].
. . . // stavke menija enum MenuItem { NEAREST = 0, LINEAR, N_MIPMAP_N, N_MIPMAP_L, L_MIPMAP_N, L_MIPMAP_L }; // Identifikatori tekstura i putanje struct TextureObjects { public const int TEXTURE_BRICK = public const int TEXTURE_FLOOR = public const int TEXTURE_CEILING = public const int TEXTURE_COUNT = } do adekvatnih slika
0; 1; 2; 3;
static int[] textures = new int[TextureObjects.TEXTURE_COUNT]; static string[] textureFiles = { "..//..//brick.jpg", "..//..//floor.jpg", "..//..//ceiling.jpg" };
122
123
124
125
Uobiajno je da se teksture koriste za simulaciju realnog okruenja (pozadine scene). Listing 6.9.3 i slika 6.9.5 prikazuju primer definisanja pozadine scene korienjem kvadra ijim stranicama su pridruene teksture pozadine.
. . . // id tekstura struct TextureObjects { public const int BACK_ID = 0; public const int FRONT_ID = 1; public const int BOTTOM_ID = 2; public const int TOP_ID = 3; public const int LEFT_ID = 4; public const int RIGHT_ID = 5; public const int TEXTURE_COUNT = 6; } static int[] textures = new int[TextureObjects.TEXTURE_COUNT]; /////////////////////////////////////////////////////////////////// // Naziv: CreateTexture // Namena: kreira mipmapping teksturu // Parametri: niz id tekstura, naziv fajla i id teksture //////////////////////////////////////////////////////////////////// static void CreateTexture(int[] textureArray, string fileName, int textureID) {
126
127
128
129
VI Open Graphics Library (OpenGL) C# OpenGL omoguava i ispis teksta kojem je pridruena tekstura. Listing 6.9.4 i slika 6.9.6 prikazuju primer korienja tekstura prilikom ispisa teksta.
. . . // rotacija teksta static float xRotation, yRotation; // display list koji sadrzi karakter fonta static int fontDL = 0; // tekstura za font static int fontTexture = 0; // bafer za smestanje karaktera windows specifican static Gdi.GLYPHMETRICSFLOAT[] gmf = new Gdi.GLYPHMETRICSFLOAT[256]; ////////////////////////////////////////////////////////////////////// // Naziv: rgCreateFont // Namena: kreiranje fonta kao display liste // Parametri: DL id, naziv fonta, velicina karak. i stil ispisa // Povratna vrednost: uspesnost kreiranja fonta ////////////////////////////////////////////////////////////////////// static bool rgCreateFont(ref int id, string name, short height, bool bold, bool italic, bool underline, bool strikeout) { id = Gl.glGenLists(256); // generisemo mesta za 256 karaktera // kreiranje Windows fonta IntPtr font = Gdi.CreateFont(-height, // velicina karaktera 0, // sirina fonta, nula da automatski je odredi 0, 0, // uglovi - zakosenost fonta (bold) ? Gdi.FW_BOLD : 0, // bold italic, // italic underline, // underline strikeout, // strikeout Gdi.DEFAULT_CHARSET, // character set identifier Gdi.OUT_TT_PRECIS, // Output Precision Gdi.CLIP_DEFAULT_PRECIS, // Clipping Precision Gdi.ANTIALIASED_QUALITY, // Output Quality Gdi.FF_DONTCARE | Gdi.DEFAULT_PITCH, // Family And Pitch name); // selektuj kreirani font kao aktivni Gdi.SelectObject(Wgl.wglGetCurrentDC(), font); bool success = Wgl.wglUseFontOutlinesW(Wgl.wglGetCurrentDC(), 0, // pocetni karakter 255, // broj DL koji se kreiraju id, // identifikator DLa 0.0f, // koliko se razlikuju od originalnog fonta 0.1f, // debljina fonta (po Z osi) Wgl.WGL_FONT_POLYGONS, // koristi poligone gmf); // bafer u koji se smestaju podaci Gdi.DeleteObject(font); return success; }
130
131
132
if (rgCreateFont(ref fontDL, "Verdana", 12, true, false, false, false) == true) { Gl.glEnable(Gl.GL_TEXTURE_2D); Gl.glBindTexture(Gl.GL_TEXTURE_2D, fontTexture); } else Environment.Exit(1); } . . .
Listing 6.9.4 Iseak programskog kda primera pridruivanja teksture tekstu U gore navedenom primeru tekst se ispisuje metode RenderTexturedText. korienjem Metode rgCreateFont i rgDeleteFont, kreiraju, odnosno briu teksturirani font. Metoda rgCreateFont kreira svaki karakter kao display list-u, korienjem metoda koje su specifine za Windows platformu: CreateFont, wglGetCurrentDC i wglUseFontOutlines. Ovaj primer demonstrira i automatsko generisanje koordinata tekstura korienjem promenljivih stanja: Slika 6.9.6 Rezultat primera iz listinga 6.9.4 Gl.GL_TEXTURE_-GEN_S i Gl.GL_TEXTURE_GEN_T. Tekst se moe ispisati korienjem tekstura (svako slovo je tekstura ili deo teksture) to omoguava da korisnikovoj maini ne mora biti podran font koji se koristi za ispis teksta, ime se mogu zaobii ogranienja koje postavlja GLUT ispis teksta. Listing 6.9.5 i slika 6.9.7 prikazuju primer ispisa teksta korienjem tekstura.
133
. . .
// display list koji sadrzi karakter fonta static int fontDL = 0; // tekstura za font static int fontTexture = 0; static int characterCount = 256; ////////////////////////////////////////////////////////////////////// // Naziv: rgCreateTextureAsFont // Namena: kreiranje "fonta" na osnovu teksture // Parametri: DL identifikator i teksture fonta, dimenzije // teksture, broj karaktera, broj redova karaktera // Povratna vrednost: uspesnost kreiranja fonta ////////////////////////////////////////////////////////////////////// static bool rgCreateTextureAsFont(ref int fontId, int textureId, int textureWidth, int textureHeight, int characterCount, int rowCount) { float xPosition, yPosition; // pozicija karaktera u teksturi int columnCount = characterCount/rowCount; int characterWidth = textureWidth/rowCount; int characterHeight = textureHeight/columnCount; fontId = Gl.glGenLists(characterCount); // kreiraj za svaki karak. DL Gl.glBindTexture(Gl.GL_TEXTURE_2D, textureId); // selektuj font tekst. for(int i = 0; i < characterCount; ++i) { // pozicija karatera xPosition = (float)(i%rowCount)/characterWidth; // X pozicija yPosition = (float)(i/columnCount)/characterHeight; // Y pozicija Gl.glNewList(fontId + i, Gl.GL_COMPILE); // nova DL za karakter Gl.glBegin(Gl.GL_QUADS); // koristimo quad na koji mapiramo teksturu karaktera // donje-levo teme Gl.glTexCoord2f(xPosition, 1.0f - yPosition 1.0f/(float)characterHeight); Gl.glVertex2i(0, 0); // gornje-levo teme Gl.glTexCoord2f(xPosition, 1.0f - yPosition - 0.001f); Gl.glVertex2i(0, characterHeight); // gornje-desno teme Gl.glTexCoord2f(xPosition + 1.0f / (float)characterWidth, 1.0f - yPosition - 0.001f); Gl.glVertex2i(characterWidth, characterHeight); // donje-desno teme Gl.glTexCoord2f(xPosition + 1.0f/(float)characterWidth, 1.0f - yPosition - 1.0f/(float)characterHeight); Gl.glVertex2i(characterWidth, 0); Gl.glEnd(); Gl.glTranslatef((float)characterWidth, 0, 0); Gl.glEndList(); // posle svake DL proveramo da li je doslo do greske if (Gl.glGetError() != Gl.GL_NO_ERROR)
134
135
136
Listing 6.9.5 Iseak programskog kda korienja tekstura za ispis teksta U gore navedenom primeru tekst se ispisuje korienjem metode RenderTextUsingTextures. Metode rgCreateTextureAsFont i rgDeleteTextureAsFont, kreiraju, odnosno briu font realizovan korienjem tekstura. Metoda rgCreateTextureAsFont kreira svaki karakter kao display list-u, koja predstavlja jedan pravougonik kome je pridruen deo teksture - karakter, slika 6.9.8. OpenGL, do verzije 2.0, postavlja Slika 6.9.7 Rezultat primera iz ogranienja na dimenzije teksture. Dimenzije listinga 6.9.5 teksture moraju biti stepen dvojke. Ako je potrebno koristiti teksture proizvoljnih dimenzija tada se koristiti ekstenzija Gl.GL_ARB_texture_rectangle. Ove teksture se mogu kreirati korienjem glTexImage2D, glSubTexImage2D, glCopyTexImage2D i glCopySubTexImage2D gde je target: Gl.GL_TEXTURE_RECTANGLE_ARB. One ne podravaju mipmapping, kao ni definisanje okvira. Od wrapping modova podrani su: Gl.GL_CLAMP, Gl.GL_CLAMP_TO_EDGE, i Gl.GL_CLAMP_TO_BORDER. S obzirom da je funkcionalnost realizovana kao ekstenzija, do Slika 6.9.8 Tekstura fonta verzije 2.0, mora se ispitati da li hardver podrava datu funkcionalnost. Sledei programski kd demonstrira ispitivanje ove funkcionalnosti:
if (Glut.glutExtensionSupported( "GL_ARB_texture_rectangle") == 0) { // ekstenzija podrzana, kreiraj teksturu Gl.glTexImage2D(Gl.GL_TEXTURE_RECTANGLE_ARB, 0, components, width, height, 0, format, type, data); }
OpenGL specifikacija verzija 2.0 je ukinula ogranienje na dimenzije teksture, tako da se ekstenzija koristi samo na hardveru koji ne podrava 2.0 specifikaciju.
137
Animacija se moe realizovati na dva naina: korienjem glutIdleFunc i/ili korienjem glutTimerFunc metoda. Metoda glutIdleFunc omoguava registrovanje callback metode koja se izvrava u pozadini izmeu dva iscrtavanja na ekran (frekvencija iscrtavanja je odreena frekvencijom osveavanja monitora). Obino se u ovu metodu postavlja programski kd koji menja parametre iscrtavanja generie se novi frejm animacije.
6.10 Animacija
public static void glutTimerFunc(int msecs, TimerCallback func, int val) public delegate void Glut.TimerCallback(int val)
gde su:
msecs period kada se poziva metoda func, func metoda koja e biti pozvana nakon isteklih msecs milisekundi, val vrednost koja se prosleuje funkciji (obino se koristi kao
identifikator koji omoguava da se ignorie tajmer). Listinzi 6.10.2 i 6.10.3, preuzeti iz [3], prikazuju isti primer (videti listing 6.5.1) u dve verzije: u kojoj se koristi tajmer i u kojoj se koristi glutIdleFunc metoda. Razlika izmeu ova dva primera se vidi u izvravanju. Drugi primer se izvrava mnogo bre, s obzirom da prvi ima tajmer od 250ms (4Hz), a drugi 60Mhz (na maini na kojoj je testiran frekvencija osveavanja ekrana!).
. . . ////////////////////////////////////////////////////////////////////// // Naziv: RenderScene // Namena: metoda iscrtava OpenGL scenu ////////////////////////////////////////////////////////////////////// static void RenderScene() { Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT); Gl.glMatrixMode(Gl.GL_MODELVIEW);
138
139
Listing 6.10.2 Primer simulacije revolucije Zemlje i Meseca oko Sunca korienjem glutIdleFunc metode
. . . //////////////////////////////////////////////////////////////////// // Naziv: Timer // Namena: animacija sa konstantnim frejmrejtom // Parametri: vrednost koja ima proizvoljnu semantiku //////////////////////////////////////////////////////////////////// static void Timer(int value) { Glut.glutPostRedisplay(); // rekurzivni poziv - periodicnost Glut.glutTimerFunc(100, Timer, value); } //////////////////////////////////////////////////////////////// // Naziv: RenderScene // Namena: metoda iscrtava OpenGL scenu //////////////////////////////////////////////////////////////// static void RenderScene() { Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT); Gl.glMatrixMode(Gl.GL_MODELVIEW); Gl.glPushMatrix(); // Pomeri se po z-osi da bi se videla scena Gl.glTranslatef(0.0f, 0.0f, -300.0f); // Nacrtaj Sunce Gl.glDisable(Gl.GL_LIGHTING); Gl.glColor3ub(255, 255, 0); Glut.glutSolidSphere(15.0f, 30, 17); Gl.glEnable(Gl.GL_LIGHTING); // Pozicioniraj (pomeri) svetlosni izvor nakom iscrtavanja Sunca Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_POSITION, lightPos); // Zarotiraj koordinatni sistem za ugao revolucije Zemlje Gl.glRotatef(earthRotation, 0.0f, 1.0f, 0.0f); // Nacrtaj Zemlju Gl.glColor3ub(0, 0, 255); Gl.glTranslatef(105.0f, 0.0f, 0.0f); Glut.glutSolidSphere(15.0f, 30, 17); // Zarotiraj koordinatni sistem za ugao revolucije Meseca Gl.glColor3ub(200, 200, 200); Gl.glRotatef(moonRotation, 0.0f, 1.0f, 0.0f); Gl.glTranslatef(30.0f, 0.0f, 0.0f); // Nacrtaj Mesec Glut.glutSolidSphere(6.0f, 30, 17); Gl.glPopMatrix(); earthRotation += 5.0f;
140
Listing 6.10.3 Primer simulacije revolucije Zemlje i Meseca oko Sunca korienjem glutTimerFunc metode Dva naina realizacije animacije u OpenGL-u nisu meusobno iskljuivi, ve se mogu kombinovati. Prilikom korienja tajmera mora se voditi rauna o broju tajmera, zbog ogranienosti resursa. U okviru ovog teksta prezentovan je osnovni deo OpenGL standarda. Za podrobnije upoznavanje sa OpenGL standardom itaocu se preporuuje [3].
LITERATURA
1. GLUT specification, http://www.opengl.org/resources/libraries/glut/glut3.spec.pdf 2. OpenGL specification, http://www.opengl.org/documentation/specs/ th 3. Wright R. Jr., Lipchak B., Haemel N., OpenGL Super Bible, 4 Ed., Addison Wesley, 2007.
141