This is an old revision of the document!


Laboratoire 06

Bande graphique

La bande graphique est une chaîne d'opérations exécutées par les processeurs GPU. Certaines de ces opérations sont décrites dans les programmes appelés shaders (eng. Shaders ), écrit par le programmeur et transmis au GPU pour l' exécution de ses processeurs. Pour les distinguer des autres opérations effectuées sur la bande graphique, que le programmeur ne peut pas modifier, les shader sont appelés “étapes programmables”. Ils offrent une grande flexibilité pour la création d’images statiques ou dynamiques avec des effets complexes rendus en temps réel (par exemple, la génération d’eau, de nuages, de feu, etc. au moyen de fonctions mathématiques).

En utilisant OpenGL sont transmises au GPU : les coordonnées des pics, les matrices de transformation des pics (M: modélisation, V: visualisation, P: projection, MV: modélisation-visualisation, MVP: modélisation-visualisation-projection), la topologie des primitives, textures et données .

1. Dans l’étape programmable, VERTEX SHADER transforme les coordonnées d’un pic à l’aide de la matrice MVP, en passant de coordonnées d’objet à coordonnées de coupe (coordonnées angulaires du clip ). En outre, des calculs d'éclairage au niveau de pointe peuvent être effectués. Le programme VERTEX SHADER est exécuté en parallèle pour un très grand nombre de pics.

2. Une étape fixe suit dans laquelle sont effectuées les opérations suivantes:

  • assembler des primitives à l'aide de sommets transformés en vertex shader et en topologie primitive;
  • élimination des filles invisibles;
  • couper les primitives à la frontière du volume de visualisation canonique ( qu'est-ce que cela signifie? );
  • partage de perspective, par lequel les coordonnées normalisées des pics de l'appareil sont calculées: xd = xc / w; yd = yc / w; zd = zc / w, où [xc, yc, zc, w] représente les coordonnées d'un pic dans le système de coordonnées de coupe;
  • transformation fenêtre-porte: de la fenêtre (-1, -1) - (1, 1) dans la fenêtre définie par le programmeur.

3. La prochaine étape est la rastérisation . Cela comprend:

  • le calcul des adresses de pixels dans lesquelles les fragments des primitives sont affichés (les morceaux de primitives égaux en taille à un pixel);
  • calcul des couleurs de chaque fragment, pour lequel le programme FRAGMENT SHADER est appelé
  • Dans l'étape programmable FRAGMENT SHADER , la couleur d'un fragment est calculée en fonction de la géométrie et des textures. le programme FRAGMENT SHADER est exécuté en parallèle pour un grand nombre de fragments.
  • test de visibilité des fragments (algorithme de tampon z);
  • opérations raster, par exemple pour combiner la couleur du fragment avec celle existant pour le pixel dans lequel le fragment est affiché.

Le résultat de l’étape de pixellisation est une image stockée dans un tableau de pixels qui sera affichée à l’écran, appelée ^^ frame buffer ^^ .

À partir de la cinquième génération de processeurs vidéo intégrés et d'OpenGL 3.x, entre les étapes 2 et 3, il existe encore une étape programmable, appelée Geometry shader .

Shader OpenGL

GLSL (GL Shading Language) est utilisé pour l'implémentation des programmes SHADER dans OpenGL.

Lier un shader au programme qui utilise OpenGL est une opération compliquée, c'est pourquoi vous avez reçu le code pour charger un shader.

Un VERTEX SHADER est un programme qui s'exécute pour TOUS les sommets envoyés à la bande graphique. Le résultat des transformations, qui représente la coordonnée post-projection du sommet traité, doit être écrit dans la variable standard gl_Position qui est ensuite utilisée par la bande graphique. Un vertex shader a toujours une fonction appelée main. Un exemple 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 est un programme qui est exécuté pour CHAQUE fragment généré à partir des opérations pour la rastérisation ( quoi? ). Fragment shader a une fonction appelée main. Un exemple de fragment de shader:

#version 330
 
layout(location = 0) out vec4 out_color;
 
void main()
{
    out_color = vec4(1, 0, 0, 0);
}

Comment attacher un objet géométrique au shader?

La connexion entre les objets (maillage, lignes, etc.) et les shaders se fait via des attributs. En raison des nombreuses versions d’OpenGL, il existe de nombreuses méthodes permettant d’établir cette connexion. En laboratoire, nous apprendrons la méthode spécifique OpenGL 3.3 et OpenGL 4.1. Les méthodes les plus anciennes ne sont plus utilisées que lorsque le matériel utilisé impose des restrictions d' API .

L'API OpenGL moderne (3.3+) utilise la méthode de liaison basée sur la présentation . Dans cette méthode, les canaux qui lient un attribut OpenGL à un nom d'attribut dans le shader sont utilisés.

glEnableVertexAttribArray(2);	
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(VertexFormat), (void*)0);

La première commande définit le canal avec le nombre 2 utilisé. La deuxième commande décrit la structure de données dans le VBO comme suit:

  • sur le tuyau 2 est envoyé au shader 3 floats (argument 3) que nous n'avons pas normalisé (argument 4)
  • l'argument 5, également appelé stride , identifie l'étape de lecture (en octets) dans le VBO pour obtenir l'attribut suivant; autrement dit, à partir de combien d'octets nous sautons quand nous voulons trouver un nouveau groupe de 3 flottants qui représentent la même chose
  • l'argument 6 identifie le décalage initial dans la mémoire tampon liée à GL_ARRAY_BUFFER (VBO); en d'autres termes, d'où nous sommes partis pour la première fois.

Dans Vertex Shader, nous obtiendrons l'attribut sur le canal avec l'index spécifié à la liaison, comme suit:

layout(location = 2) in vec3 vertex_attribute_name;

Plus d'informations sont disponibles sur la page de documentation de l' index d'attribut Vertex Shader Vertex Shader attribute index.

Pour plus de détails, vous pouvez accéder à:

Un article sur l'histoire compliquée d'OpenGL et la concurrence avec Direct3D / DirectX peut être lu ici.

Comment envoyer des données générales à un shader?

À un shader, nous pouvons envoyer des données de la CPU via des variables uniformes. Ils sont appelés uniformes car ils ne varient pas pendant l'exécution du shader . Pour envoyer des données à une variable du shader, vous devez obtenir l'emplacement de la variable dans le programme du shader à l'aide de la fonction glGetUniformLocation:

int location = glGetUniformLocation(int shader_program, "uniform_variable_name_in_shader");
  • shader_program représente l'ID du programme shader compilé sur la carte vidéo
  • dans le cadre du laboratoire, l'identifiant peut être obtenu en appelant la fonction shader→GetProgramID()ou en accédant directement au membre viableshader→program

Ensuite, après avoir l'emplacement (qui représente un décalage / un pointeur), nous pouvons envoyer à ce pointeur des informations avec des fonctions de type 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:

  • Matrix - in cazul in care e prezent identifica o matrice
  • N - reprezinta numarul de variabile de tipul T ce vor fi trimise:
    • 1, 2, 3, 4 in cazul tipurilor simple
    • pentru matrici mai exista si 2×3, 2×4, 3×2, 3×4, 4×2, 4×3
  • T - reprezinta tipul variabilelor trimise
    • ui - unsigned int
    • i - int
    • f - float
  • v - datele sunt specificate printr-un vector, se da adresa de memorie a primei valori din vector

Comunicarea intre shadere-le OpenGL

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

In metoda specifica OpenGL 3.3 numele de atribut attribute_name trebuie sa fie acelasi atat in Vertex Shader cat si in Fragment Shader pentru a se stie legatura intre input/output.

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;

In caz ca avem support pentru GLSL 410 (OpenGL 4.1) se poate specifica si locatia attributului astfel, caz in care doar locatiile vor fi folosite pentru a lega iesirea unui Vertex Shader de intrarea la Fragment Shader si nu numele atributului.
Mai multe detalii se pot obtine de la: Program separation linkage

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;

Cerinte laborator

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.

  1. Completati functia RenderSimpleMesh astfel inca sa trimiteti corect valorile uniform catre Shader
    • Se interogeaza locatia uniformelor “Model”, “View” si “Projection”
    • Folosind glUniformMatrix4fv sa se trimita matricile corespunzatoare catre shader
    • Daca ati completat corect functia, si ati completat gl_Position in vertex shader, ar trebui sa vedeti un cub pe centrul ecranului rottit 45 grade in jurul lui Y si colorat variat
  2. Completati Vertex Shaderul
    1. Se de clara atributele de intrare pentru Vertex Shader folosind layout location
      	layout(location = 0) in vec3 v_position;
      	// same for the rest of the attributes ( check Lab6.cpp CreateMesh() );
    2. Se declara atributele de iesire catre Fragment Shader
      	out vec3 frag_color;
      	// same for other attributes
    3. Se salveza valorile de iesire in main()
      	frag_color = vertex_color;
      	// same for other attributes
    4. Se calculeaza pozitia in clip space a vertexului primit folosind matricile Model, View, Projection
      	gl_Position = Projection * View * Model * vec4(v_position, 1.0);
  3. Completati Fragment Shaderul
    • Se primesc valorile atributelor trimise de la Vertex Shader
    • Valoarea de intrare ale fiecarui atribut e calculata prin interpolare liniara intre vertexii ce formeaza patch-ul definit la desenare (triunghi, linie)
      	in vec3 frag_color;
    • Se calculeaza valoarea fragmentului (pixelului) de output
      	out_color = vec4(frag_color, 1);
  4. Sa se utilizeze normala vertexilor pe post de culoare de output in cadrul Fragment Shader-ului
    • Inspectati de asemenea structura VertexFormat pentru a intelege ceea ce se trimite pe fiecare pipe
  5. Sa se interschimbe pipe-ul 1 cu pipe-ul 3. Trimiteti normala pe pipe-ul 3 si culoarea vertexului pe pipe-ul 1
    • Se inspecteaza rezultatul obtinut
  6. Bonus: sa se trimita timpul aplicatiei (Engine::GetElapsedTime()), si sa se varieze pozitia si culoarea (unul sau mai multe canale de culoare) dupa o functie de timp (trigonometrica etc.)
egc/laboratoare/fr/06.1573708863.txt.gz · Last modified: 2019/11/14 07:21 by alexandru.gradinaru
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0