This is an old revision of the document!
Banda Grafică este un lanț de operații executate de procesoarele GPU. Unele dintre aceste operații sunt descrise în programe numite shadere (eng. shaders), care sunt scrise de programator și transmise la GPU pentru a fi executate de procesoarele acestuia. Pentru a le deosebi de alte operații executate în banda grafică, pe care programatorul nu le poate modifica, shaderele sunt numite „etape programabile”. Ele dau o mare flexibilitate în crearea de imagini statice sau dinamice cu efecte complexe redate în timp real (de ex. generarea de apă, nori, foc etc prin funcții matematice).
Folosind OpenGL sunt transmise la GPU: coordonatele vârfurilor, matricile de transformare ale varfurilor (M: modelare, V: vizualizare, P: proiecție, MV: modelare-vizualizare, MVP: modelare-vizualizare-proiecție), topologia primitivelor, texturi și ale date.
1. În etapa programabilă VERTEX SHADER se transformă coordonatele unui vârf, folosind matricea MVP, din coordonate obiect în coordonate de decupare (eng. clip coordinates). De asemenea, pot fi efectuate și calcule de iluminare la nivel de vârf. Programul VERTEX SHADER este executat în paralel pentru un număr foarte mare de vârfuri.
2. Urmează o etapă fixă, în care sunt efectuate următoarele operații:
3. Următoarea etapă este Rasterizarea. Aceasta include:
Rezultatul etapei de rasterizare este o imagine memorată într-un tablou de pixeli ce va fi afișat pe ecran, numit ^^frame buffer^^.
Pentru implementarea de programe SHADER în OpenGL se folosește limbajul dedicat GLSL (GL Shading Language).
Legarea unui shader la programul care folosețte OpenGL este o operație complicată, de aceea vă este oferit codul prin care se încarcă un shader.
Un VERTEX SHADER e un program care se execută pentru FIECARE vertex trimis către banda grafică. Rezultatul transformărilor, care reprezintă coordonata post-proiecție a vertexului procesat, trebuie scris în variabila standard gl_Position care e folosită apoi de banda grafică. Un vertex shader are tot timpul o funcție numită main. Un exemplu de vertex shader:
#version 330 layout(location = 0) in vec3 v_position; // Uniform properties uniform mat4 Model; uniform mat4 View; uniform mat4 Projection; void main() { gl_Position = Projection * View * Model * vec4(v_position, 1.0); }
Un FRAGMENT SHADER e un program ce este executat pentru FIECARE fragment generat în urma operației de rasterizare (ce înseamnă?). Fragment shader are în mod obligatoriu o funcție numită main. Un exemplu de fragment shader:
#version 330 layout(location = 0) out vec4 out_color; void main() { out_color = vec4(1, 0, 0, 0); }
Legarea între obiecte (mesh, linii etc.) și shadere se face prin atribute. Datorită multelor versiuni de OpenGL există multe metode prin care se poate face această legare. În laborator vom învăța metoda specifică OpenGL 3.3 și OpenGL 4.1. Metodele mai vechi nu mai sunt utilizate decât atunci când hardware-ul utilizat impune restricții de API.
API-ul OpenGL modern (3.3+) utilizează metoda de legare bazată pe layout-uri. În această metodă se folosesc pipe-uri ce leagă un atribut din OpenGL de un nume de atribut în shader.
glEnableVertexAttribArray(2); glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(VertexFormat), (void*)0);
Prima comandă setează pipe-ul cu numărul 2 ca fiind utilizat. A doua comandă descrie structura datelor în cadrul VBO-ului astfel:
În Vertex Shader vom primi atributul respectiv pe pipe-ul cu indexul specificat la legare, astfel:
layout(location = 2) in vec3 vertex_attribute_name;
Mai multe informații se pot găsi pe pagina de documentație Vertex Shader attribute index.
La un shader putem trimite date de la CPU prin variabile uniforme. Se numesc uniforme pentru că nu variază pe durata executiei shader-ului. Ca să putem trimite date la o variabilă din shader trebuie să obținem locația variabilei în programul shader cu funcția glGetUniformLocation:
int location = glGetUniformLocation(int shader_program, "uniform_variable_name_in_shader");
shader→GetProgramID()
sau direct accesând vriabila membru shader→program
Apoi, dupa ce avem locatia (care reprezinta un offset/pointer) putem trimite la acest pointer informatie cu functii de tipul glUniform:
//void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) glm::mat4 matrix(1.0f); glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(matrix)); // void glUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) glUniform4f(location, 1, 0.5f, 0.3f, 0); //void glUniform3i(GLint location, GLint v0, GLint v1, GLint v2) glUniform3i(location, 1, 2, 3); //void glUniform3fv(GLint location, GLsizei count, const GLfloat *value) glm::vec3 color = glm::vec3(1.0f, 0.5f, 0.8f); glUniform3fv(location, 1, glm::value_ptr(color));
Functiile glUniform sunt de forma glUniform[Matrix?]NT[v?] (regex) unde:
In general pipeline-ul programat este alcatuit din mai multe programe shader. In cadrul cursului de EGC vom utiliza doar Vertex Shader si Fragment Shader. OpenGL ofera posibilitatea de a comunica date intre programele shader consecutive prin intermendiul atributelor in si out
Vertex Shader:
#version 330 // GLSL version of shader (GLSL 330 means OpenGL 3.3 API) out vec3 attribute_name;
Fragment Shader:
in vec3 attribute_name;
Vertex Shader:
#version 410 // GLSL 410 (OpenGL 4.1 API) layout(location = 0) out vec4 vertex_out_attribute_name;
Fragment Shader:
#version 410 layout(location = 0) in vec4 fragment_in_attribute_name;
tasta F5 - reincarca shaderele in timpul rularii aplicatiei. Nu este nevoie sa opriti aplicatia intrucat shaderele sunt compilate si rulate de catre placa video si nu au legatura cu codul sursa C++ propriu zis.
RenderSimpleMesh
astfel inca sa trimiteti corect valorile uniform catre ShaderglUniformMatrix4fv
sa se trimita matricile corespunzatoare catre shaderVertex Shader
folosind layout location layout(location = 0) in vec3 v_position; // same for the rest of the attributes ( check Lab6.cpp CreateMesh() );
Fragment Shader
out vec3 frag_color; // same for other attributes
main()
frag_color = vertex_color; // same for other attributes
gl_Position = Projection * View * Model * vec4(v_position, 1.0);
Vertex Shader
in vec3 frag_color;
out_color = vec4(frag_color, 1);
VertexFormat
pentru a intelege ceea ce se trimite pe fiecare pipe