Tampons de trame
Notions de base sur les framebuffers
Framebuffer est un type de tampon qui stocke les valeurs de couleur, les informations de profondeur et de pochoir des pixels en mémoire. Lorsque vous dessinez quelque chose dans OpenGL, la sortie est stockée dans le * tampon de trame par défaut * et vous voyez alors les valeurs de couleur de ce tampon à l’écran. Vous pouvez également créer votre propre framebuffer qui peut être utilisé pour de nombreux effets de post-traitement sympas tels que * échelle de gris, flou, profondeur de champ, distorsions, reflets *…
Pour commencer, vous devez créer un objet framebuffer (FBO) et le lier comme n’importe quel autre objet dans OpenGL :
unsigned int FBO;
glGenFramebuffers(1, &FBO);
glBindFramebuffer(GL_FRAMEBUFFER, FBO);
Vous devez maintenant ajouter au moins une pièce jointe (couleur, profondeur ou pochoir) au framebuffer. Une pièce jointe est un emplacement mémoire qui agit comme un tampon pour le framebuffer. Il peut s’agir soit d’une texture, soit d’un objet renderbuffer. L’avantage d’utiliser une texture est que vous pouvez facilement utiliser cette texture dans un shaders de post-traitement. La création de la texture est similaire à une texture normale :
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
La “largeur” et la “hauteur” doivent être identiques à la taille de votre fenêtre de rendu. Le pointeur de données de texture est NULL
car vous souhaitez uniquement allouer de la mémoire et ne pas remplir la texture avec des données. La texture est prête, vous pouvez donc l’attacher au framebuffer :
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
Votre framebuffer devrait être prêt à l’emploi maintenant, mais vous pouvez également ajouter un accessoire de profondeur ou des accessoires de profondeur et de pochoir. Si vous souhaitez les ajouter en tant que pièces jointes de texture (et les utiliser pour certains traitements), vous pouvez créer d’autres textures comme ci-dessus. La seule différence serait dans ces lignes:
glTexImage2D(
GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0,
GL_DEPTH_COMPONENT, GL_FLOAT, NULL
);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texture, 0);
Ou ceux-ci si vous souhaitez utiliser la profondeur ** et ** l’attachement du pochoir dans une seule texture :
glTexImage2D(
GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width, height, 0,
GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL
);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, texture, 0);
Vous pouvez également utiliser un renderbuffer au lieu d’une texture comme pièce jointe pour les tampons de profondeur et de gabarit si vous ne souhaitez pas traiter les valeurs ultérieurement. (Ce sera expliqué dans un autre exemple…)
Vous pouvez vérifier si le framebuffer est créé avec succès et terminé sans aucune erreur :
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE)
// do something...
Et enfin, n’oubliez pas de dissocier le framebuffer afin de ne pas y rendre accidentellement :
glBindFramebuffer(GL_FRAMEBUFFER, 0);
Limites
Le nombre maximal de tampons de couleur pouvant être attachés à un seul tampon de trame peut être déterminé par la fonction OGL glGetIntegerv , en utilisant le paramètre GL_MAX_COLOR_ATTACHMENTS
:
GLint maxColAttchments = 0;
glGetIntegerv( GL_MAX_COLOR_ATTACHMENTS, &maxColAttchments );
Utilisation du framebuffer
L’utilisation est assez simple. Tout d’abord, vous liez votre ** framebuffer ** et y rendez votre scène. Mais vous ne verrez encore rien car votre renderbuffer n’est pas visible. La deuxième partie consiste donc à rendre votre framebuffer sous la forme d’une texture d’un quad plein écran sur l’écran. Vous pouvez simplement le rendre tel quel ou faire des effets de post-traitement.
Voici les sommets pour un quad plein écran :
float vertices[] = {
// positions texture coordinates
-1.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f
};
Vous devrez les stocker dans un VBO ou effectuer un rendu à l’aide de pointeurs d’attribut. Vous aurez également besoin d’un programme de shader de base pour rendre le quad plein écran avec texture.
Ombrage de sommet :
in vec2 position;
in vec2 texCoords;
out vec2 TexCoords;
void main()
{
gl_Position = vec4(position.x, position.y, 0.0, 1.0);
TexCoords = texCoords;
}
Nuanceur de fragment :
in vec2 TexCoords;
out vec4 color;
uniform sampler2D screenTexture;
void main()
{
color = texture(screenTexture, TexCoords);
}
Remarque : Vous devrez peut-être ajuster les shaders pour votre version de GLSL.
Vous pouvez maintenant faire le rendu proprement dit. Comme décrit ci-dessus, la première chose à faire est de rendre la scène dans votre FBO. Pour ce faire, il vous suffit de lier votre FBO, de l’effacer et de dessiner la scène :
glBindFramebuffer(GL_FRAMEBUFFER, FBO);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// draw your scene here...
Remarque : Dans la fonction “glClear”, vous devez spécifier toutes les pièces jointes de framebuffer que vous utilisez (dans cet exemple, la couleur et la profondeur de la pièce jointe).
Vous pouvez maintenant rendre votre FBO en quad plein écran sur le framebuffer par défaut afin que vous puissiez le voir. Pour ce faire, il vous suffit de dissocier votre FBO et de rendre le quad :
glBindFramebuffer(GL_FRAMEBUFFER, 0); // unbind your FBO to set the default framebuffer
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shader.Use(); // shader program for rendering the quad
glBindTexture(GL_TEXTURE_2D, texture); // color attachment texture
glBindBuffer(GL_ARRAY_BUFFER, VBO); // VBO of the quad
// You can also use VAO or attribute pointers instead of only VBO...
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindBuffer(GL_ARRAY_BUFFER, 0);
Et c’est tout! Si vous avez tout fait correctement, vous devriez voir la même scène qu’avant mais rendue sur un quad plein écran. La sortie visuelle est la même qu’auparavant, mais vous pouvez désormais facilement ajouter des effets de post-traitement en modifiant simplement le shader de fragment. (Je vais ajouter des effets dans un ou plusieurs autres exemples et les lier ici)