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));

Les fonctions glUniform ont la forme glUniform[Matrix?]NT[v?] (regex) où:

  • Matrix - si présent identifie un tableau
  • N - représente le nombre de variables de type T à envoyer:
    • 1, 2, 3, 4 pour les types simples
    • pour les matrices il y a aussi 2×3, 2×4, 3×2, 3×4, 4×2, 4×3
  • T - représente le type de variables envoyées
    • ui - unsigned int
    • i - int
    • f - float
  • v - les données sont spécifiées par un vecteur, l'adresse mémoire de la première valeur du vecteur est donnée

Communication entre shaders OpenGL

n général, le pipeline planifié se compose de plusieurs programmes de shader. Dans le cours EGC, nous utiliserons uniquement Vertex Shader et Fragment Shader . OpenGL offre la possibilité de communiquer des données entre des programmes de shader consécutifs via les attributs in et out.

OpenGL 3.3 attribut nom de méthode spécifique ATTRIBUTE_NAME doit être identique à la fois Vertex Shader et le fragment shader pour connaître la connexion entre l' entrée / sortie.

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;

Si GLSL 410 (OpenGL 4.1) est pris en charge, l'emplacement de l'attribut peut également être spécifié. Dans ce cas, seuls les emplacements seront utilisés pour lier la sortie d'un Vertex Shader à l'entrée Fragment Shader et non le nom de l'attribut. Plus de détails peuvent être obtenus auprès de: 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;

Exercices

Touche F5 - recharge le shader lors de l'exécution de l'application. Il n'est pas nécessaire d'arrêter l'application car les shader sont compilés et exécutés par la carte vidéo et ne sont pas liés au code source C ++ lui-même.

  1. Completez la fonction RenderSimpleMesh pour que les valeurs soient correctement envoyées à Shader
    • L'emplacement des uniformes “Modèle”, “Vue” et “Projection” est interrogé
    • Utilisation glUniformMatrix4fv des tableaux appropriés à envoyer au shader
    • Si vous avez terminé la fonction correctement et que vous avez terminé gl_Position dans le vertex shader, un cube au centre de l'écran pivoté à 45 degrés autour de Y et de couleur différente
  2. Completez le Vertex Shader
    1. Il efface les attributs d'entrée pour Vertex Shader utiliser l'emplacement de la disposition
      	layout(location = 0) in vec3 v_position;
      	// same for the rest of the attributes ( check Lab6.cpp CreateMesh() );
    2. Les attributs de sortie à Fragment Shader sont déclarés
      	out vec3 frag_color;
      	// same for other attributes
    3. Les valeurs de sortie sont enregistrées dans main()
      	frag_color = vertex_color;
      	// same for other attributes
    4. La position dans le clip reçu du sommet reçu est calculée à l'aide des matrices Modèle, Vue, Projection.
      	gl_Position = Projection * View * Model * vec4(v_position, 1.0);
  3. Copmletez le fragment Shaderul
    • Les valeurs des attributs envoyés de Vertex Shader
    • La valeur d'entrée de chaque attribut est calculée par interpolation linéaire entre les sommets formant le patch défini au dessin (triangle, ligne).
      	in vec3 frag_color;
    • La valeur du fragment de sortie (pixel) est calculée
      	out_color = vec4(frag_color, 1);
  4. Pour utiliser le sommet normal en tant que couleur de sortie dans le Fragment Shader
    • Inspectez également la structure VertexFormatpour comprendre ce qui est envoyé sur chaque pipe.
  5. Échangez le pipe 1 avec le pipe 3 . Envoi normal sur le pipe 3 et couleur de sommet sur le pipe 1
    • Le résultat obtenu est inspecté
  6. Bonus: envoyez le temps d'application (Engine :: GetElapsedTime ()), et changez la position et la couleur (un ou plusieurs canaux de couleur) après une fonction de temps (trigonométrique, etc.)
egc/laboratoare/fr/06.txt · Last modified: 2019/11/14 07:33 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