Kolegij Računalna grafika, 2018/19, Student: Vedran Ivanušid, 0036477033
Dokumentacija za 1. Laboratorijsku vježbu: Pradenje putanje
Učitavanje tijela
Učitani model čuva se u objektu tipa DrawableObject, a objekt se učitava funkcijom loadOBJ koja je
deklarirana u obj_loader.h, a definira se u utils.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class DrawableObject{
public:
DrawableObject(std::vector<glm::vec3> vert, std::vector<Face> fc);
void draw();
void rotate(glm::mat3 rot);
void translate(glm::vec3 v);
glm::vec3 center;
private:
std::vector<glm::vec3> vertices;
std::vector<glm::vec3> vertices_original;
std::vector<Face> faces;
};
1.1 Aproksimacijska uniformna B-splajn krivulja
- Učitava se u metodi ucitajBspline(spline_path);
Metoda ucitajBspline obrađuje segment po segment krivulje (segment se sastoji od četiri točke),
a i-ti segment krivulje parametar t se računa kao k/100, gdje je k cijeli broj [0-100].
Za i-ti segment i j-tu točku segmenta računa se pozicija, prva i druga derivacija. Opdenito, ako
segment aproksimacijske B-splajn krivulje ima k točaka, a postoji N segmenata, onda imamo N*k
točaka krivulje koje se u programu čuvaju u globalno definiranom vektoru splineB. Takđer,
prilikom učitavanja, odmah računamo i prve i druge derivacije u točki. One se čuvaju u vektorima
tanB, dtanB.
Metode koje računaju poziciju, prvu i drugu derivaciju redom: pozicija, tangenta, derivacija2
1.4 Rotacija objekta
Za rotaciju objekta koriste se četiri parametra: os rotacije(3 parametra) i kut rotacije.
3 parametra x,y,z osi rotacije dobijemo kao vektorski umnožak početne osi rotacije( u smjeru z-osi
0,0,1) i željene osi rotacije koja se dobije kao vektor tangente u trenutnoj točki.
Kosinus kuta rotacije dobijemo iz skalarnog umnoška početnog vektora rotacije s i konačnog vektora
rotacije, e, koji još podijelimo s njihovim normama.
cos𝜑 =𝑠𝑒
𝑠 |𝑒|
Dodatno, potrebno je pretvoriti kut rotacije iz radijana u stupnjeve.
glm::vec3 os = glm::cross(s, e); float se = glm::dot(s, e); float aps_s = sqrt(pow(s.x, 2) + pow(s.y, 2) + pow(s.z, 2)); float aps_e = sqrt(pow(e.x, 2) + pow(e.y, 2) + pow(e.z, 2)); float fi_rad = acos(se / (aps_s * aps_e)); float fi_stup = (fi_rad / (2 * N_PI)) * 360.0f;
Pomak objekta na krivulji radi se tako da se za svaku točku bSpline-a, ovdje k-tu točku:
1. Objekt pomakne u ishodište glTranslatef(splineB[k].x, splineB[k].y, splineB[k].z);
2. Objekt se rotira u pozitivnoj smjeru sa glRotatef(fi_stup, os.x, os.y, os.z)
oko osi rotacije
3. Objekt se vrati nazad na poziciju glTranslatef(-obj_to_draw->center.x, -
obj_to_draw->center.y, -obj_to_draw->center.z)
4. Objekt se iscrta sa obj_to_draw->draw()
Slika: Wireframe avion polijede po B-spline krivulji. Iscrtane su koordinatne osi. Na slici desno vide se
tangente u nekoliko točaka na krivulji.
Namještanje putanje do objekta i spline-a:
- Direktorij s modelima: /models
- Direktorij s putanjama: /splines
char* model_path = "../Zadatak1/models/aircraft747.obj"; char* spline_path = "../Zadatak1/splines/bspline_runway.txt";
Dokumentacija za 2. Laboratorijsku vježbu
2.1 Definicija čestice / Particle.h
Čestica je definirana kao c++ struct sa atributima početna pozicija, brzina, akceleracija, trenutna
pozicija, brzina, akceleracija, sila, boja, veličina, starost, životni vijek i zastavica koja označava da li je
čestica živa, tj. kažemo da je čestica živa ako (p_age < p_lifeSpan).
Metoda update čestice ažurira njenu poziciju i brzinu. Nakon svakog poziva particle::update za neku
česticu, starost te čestice se povedava za neki fiksni prethodno zadani vremenski interval (fTimeStep).
void Particle::update(float fTimeStep) { p_age += fTimeStep; p_velocity += p_acceleration; p_position = p_init_position + p_init_velocity * p_age + 0.5f * p_init_acc* glm::pow(p_age, 2); return; }
ParticleSystem
Sustav čestica( ParticleSystem.h i ParticleSystem.cpp). Ova klasa ima pokazivač na primjerak klasa
ParticleEmitter i ParticleBuffer, gdje je ParticleBuffer definira tip podatka kao vektor unique
pokazivača na objekte tipa Particle (std::vector<std::unique_ptr<Particle>>). Ova definicija
može se pronadi u Particle.h.
Ukoliko želimo npr. koristiti sferni emiter, sve što je potrebno je naslijediti klasu ParticleEmitter i
definirati novi emiter. Prilikom inicijalizacije sustava čestice predati pokazivač na takav sferni emiter.
Klasa particle system obavlja i druge 'kudanske poslove' kao što su stvaranje čestica, inicijalizacija
čestica: u metodi ParticleSystem::InitializeParticles() se zove metoda
ParticleEmitter::EmitParticle(Particle& particle) koja prima referencu na česticu i modificira inicijalno
njenu poziciju,brzinu; ParticleSystem ažurira sve čestice nakon nekog vremenskog intervala u metodi
ParticleSystem::Update(float fTimeStep).
Detaljnjiji opis glavnih metoda slijedi.
ParticleSystem::Initialize()
- Zove metodu CreateParticles, koja stvara potreban broj čestica (alocira memoriju) i stvara
pokazivače na stvorene objekte tipa Particle.
- Zove InitializeParticles() -> svakoj čestici daje se početni položaj, brzina, akceleracija kroz
metodu EmitParticle() odgovoarajudeg emitera.
- Stvara VAO, VBO, EBO buffer objekte koji de se koristiti. Pozivom metode BindBuffers() vežu
se bufferi ovog ParticleSystema na odgovarajude targete.
- Puni GL_ARRAY_BUFFER i GL_ELEMENT_ARRAY_BUFFER spremnike, za prvi se postavlja
GL_DYNAMIC_DRAW kao flag, jer se podaci često mjenjaju
- Postavlja pokazivače za VBO (linija 116-140)
ParticleSystem::makeVertexBuffer()
- u ovoj metodi pripremaju se podaci koji de se spremati u GL_ARRAY_BUFFER
- računa se orijentacija Billboard čestica (tako da su uvijek prema promatraču)
- Za svaku česticu: računa se os rotacije kao vektorski produkt početne orijentacije s (uvijek u
smjeru –z osi ) i vektora između kamere i trenutne pozicije čestice - e. Potom se računa
kosinus kuta rotacije iz skalarnog produkta vektora (s, e). Konačno, za svaku česticu računa se
matrica rotacije, za izračunati kut, oko dobivene osi rotacije.
- Računaju se pozicije 4 točke poligona na koji de se primjeniti tekstura iz: pozicije čestice,
matrice rotacije R, relativnog položaja točke s obzirom na centar čestice, npr. donji lijevi rub
poligona ,x=-0.5f, y=-0.05f: glm::vec3 tmp1 = p->p_position + glm::vec3(R * ((-X - Y)));
- Na ovaj način stvorit de se ukupno N * 4 * 8 = N * 32 atributa za N čestica (3 za poziciju, 3 za
boju, 2 za koordinatu teksture) i sve se množi s 4 jer imamo 4 rubne točke poligona.
- Ova metoda modificira član objekta VertexBuffer, svaki put kad želimo raditi promjene
čestica. Treba napomenuti da se memorijski prostor nikad više ne mjenja tokom života
jednog sustava čestica (Kad se čestica rodi, iskoristi se memorijski element neke umrle
čestice).
Slika: dijagram klasa i struktura
Slika: Efekt vatrometa s dva sustava emitera čestica.
Dokumentacija za 3. Samostalnu vježbu
Naziv: Demonstracija uporabe shadera za rotaciju i uzorkovanje tekstura
Ciljevi:
Demonstrirati na koji način se koriste VBO(Vertex buffer object), VAO(vertex array object),
EBO(element buffer object)
Učitvanje teksture
Pisanje vertex i fragment shadera, te njihova svrha i uporaba
Kompajliranje shadera iz source koda
Kako dohvatiti atribute na shaderu
Potrebne biblioteke:
Biblioteka za učitavanje OpenGL ekstenzija: glew
Biblioteka za I/O, inicijaliziranje OpenGL konteksta: freeglut
Stb – niz utility biblioteka za C/C++, korišten za učitavanje teksture: stb_image.h
https://github.com/nothings/stb
Pokretanje vježbe
Ako ved nije uključeno u root folderu projekta treba uključiti GLConfig_relative.props u kojem su
podešene staze do svih potrebnih biblioteka s obzirom na glavni direktorij gdje se nalazi Visual Studio
solution (RG2018.sln). Sve potrebne biblioteke su uključene u direktorije projekta, nije potrebno
raditi dodatna podešavanja.
VBO (Vertex buffer object)
- OpenGL objekti, izvori (spremnici, bufferi) Vertex podataka, jedan vertex buffer object može
definirati više vertex atributa (pozicija, boja, normale, 2D teksturne kordinate)
- U našem primjeru definirali smo u jednom bufferu slijedno 3 vrijednosti za poziciju, 3 za boju
(rgb), te 2 za teksturu: PPP BBB TT.
glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer: Omoguduje vezanje spremnika na „target“. Ovo označava nakanu da želim koristiti
spremnik VBO za spremanje podataka vertex atributa. Bududi pozivi funkcija koriste vertex atribute
gledaju koji je buffer trenutno vezan na GL_ARRAY_BUFFER.
VAO(Vertex Array Object)
- Prednosti: ne moramo svaki put prije iscrtavanja raditi isti posao, VAO se „veže“ na kontekst i
pamti stanje konteksta. Pozivi nekih od navedenih funkcija modificiraju stanje VAO-a:
- glBindBuffer, glEnableVertexAttribArray, glVertexAttribPointer
- npr. pozivom glVertexAttribPointer VAO pamti koji buffer je bio vezan na GL_ARRAY_BUFFER
za taj atribut i pamti definirani pointer. Bududi pozivi na glBindBuffer se ne pamter, jer VAO
pamti samo buffer koji je bio vezan u trenutku poziva glVertexAttribPointer funkcije.
- Generiranje VAO objekta: glGenVertexArrays(1, &VAO);
- Prije crtanja koristimo: glBindVertexArray(VAO);
EBO(Element Buffer Object)
- Omoguduje 'recikliranje' vertexa zadavanjem indeksa vertexa koji treba iscrtati.
- Način inicijalizacije je identičan kao za VBO:
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices,GL_STATIC_DRAW);
- Poziv funkcije glDrawElements koristi GL_ELEMENT_ARRAY_BUFFER – indeksirano crtanje glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
- Prvi parametar je primitiv kojeg želimo crtati, drugi parametar je broj
elemenata(ne broj primitiva), odnosno crtamo 12 trokuta, koji se svaki sastoji
od 3 vertexa danih indeksima u GL_ELEMENT_ARRAY_BUFFER.
Shaderi
- VertexShader i FragmentShader dio su OpenGL grafičkog cijevovoda. OpenGL zahtjeva od
nas da definiramo barem VertexShader i FragmentShader (ne postoje podrazumijevani).
U kodu VertexShader je definiran u datoteci VertexShaders.h, a fragment shader u
FragmentShaders.h
Vertex Shader
- GLSL jezik
- Koriste se za modificiranje svojstava vertexa: pozicija, boja, koordinate teksture
- Definicija „atributa“ na shaderu: layout(location = i) in ...
#version 330 core layout(location = 0) in vec3 aPos; layout(location = 1) in vec3 aColor; layout(location = 2) in vec2 aTexCoord; out vec3 ourColor; out vec2 TexCoord; uniform mat4 u_proj; uniform mat4 u_view; uniform mat4 u_model; void main() { gl_Position = u_proj * u_view * u_model * vec4(aPos, 1.0); ourColor = aColor; TexCoord = aTexCoord; }
FragmentShader
- Obrađuje fragment generiran rasterizacijskim korakom i generira novi (modificirani)
fragment
- Novi fragment sadrži depth value, stencil value, podatke o boji
Korišteni fragment shader:
#version 330 core out vec4 FragColor; in vec3 ourColor; in vec2 TexCoord; uniform sampler2D ourTexture; void main() { FragColor = texture(ourTexture, TexCoord) * vec4(ourColor, 0.5); };
- Ukoliko imamo jednu izlaznu varijablu ne moramo specificirati lokaciju, postavlja se na
lokaciju 0, mogli smo pisati layout(location=0) out vec4 FragColor
Prije korištenja Vertex i Fragment shader moramo kompilirati:
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); glCompileShader(fragmentShader); checkShaderCompileStatus(fragmentShader);
Nakon što to napravimo za oba shadera, potrebno je dodati oba shadera u naš shader program, te dodatno linkati
program. Linkanjem se stvaraju izvršni programi koji de se izvršavati u vertex, odnosno fragment procesoru.
ourShader = glCreateProgram(); glAttachShader(ourShader, vertexShader); glAttachShader(ourShader, fragmentShader);
glLinkProgram(ourShader);
Ako sad želimo koristiti naš shader program:
glUseProgram(ourShader);
Ako želimo slati podatke na shader:
Dohvatimo lokaciju varijable – glGetUniformLocation(program, ime) vrada cjeli broj koji određuje
lokaciju varijable sa danim imenom unutar specificiranog programa. Npr. projekcijska matrica:
GLint proj_loc = glGetUniformLocation(ourShader, "u_proj");