Tuto OpenGL : les FBO


Ceci est du réchauffage micro-ondes d'un vieux tuto qui avait sauté lors du passage à WordPress.

Introduction

Ce tutorial s'inscrit dans la lignée du tutorial sur les pbuffers, disponible [url="http://www.coder-studio.com/"]ici[/url] (NDLR : que je n'ai pas reporté tellement c'est vieux).
Le but est toujours le même : permettre d'enregistrer une scène sur une texture rapidement, afin de pouvoir réaliser moult joulis gfx ;)
Nous avions vu que si les pbuffers étaient performants, ils n'existaient que sous Linux. Une extension plus récente a depuis fait apparition, ce sont les framebuffer objets. L'API pour y accéder a été normalisée par l'ARB mi-2004. C'est une extension complexe, la spec fait un demi-Mo de texte brut. Mais n'ayez pas peur, ça va bien se passer :)

Une caractéristique agréable du fbo est qu'elle permet, si toutefois la carte graphique le supporte, de dessiner sur des NPOTS : Non Power Of Two textures. Autrement dit, si votre écran fait 800*600, vous pouvez sans problème aucun avoir une texture de 800*600, dessiner dessus, puis l'utiliser comme une texture normale, au lieu de devoir utiliser une 1024x1024 et d'en perdre une bonne partie.

Nous allons donc voir comment utiliser ces textures rectangulaires, puis comment utiliser les fbos.

Les textures rectangulaires

Il s'agit ni plus ni moins d'une texture normale, très banale. La seule différence avec GL_TEXTURE_2D est son nom : GL_TEXTURE_RECTANGLE_ARB.
Sans plus attendre, voiçi un exemple complet de création d'une texture vide.

	glEnable(GL_TEXTURE_RECTANGLE_ARB);
	int taille_max;
	glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB,&taille_max);
	std::cout << " Taille max de texture supportée : " << taille_max << std::endl;
	GLuint rect_tex;
	glGenTextures(1,&amp;rect_tex);
	glBindTexture(GL_TEXTURE_RECTANGLE_ARB,rect_tex); // Exactement comme pour les tex2D
	glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB8, 800, 600, 0, GL_RGB, GL_INT, NULL);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);

Avant de continuer, quelques remarques nécessaires sur les NPOTS.
Il est impossible de créer des mipmaps. Ainsi, il ne faut jamais appeller glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST); ou assimilé.
Elles ne supportent que ces modes de filtrage: GL_CLAMP, GL_CLAMP_TO_EDGE, and GL_CLAMP_TO_BORDER.
Elles ne peuvent avoir de bordure, mais franchement, ce n'est pas bien grave.
Il y a des priorités dans les types de textures. De la moins forte à la plus forte: GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP. Autrement dit: impérativement désactiver les tex3D et les cubemaps avant d'utiliser les texRect !!!
Les texRect sont répandues. Mais pour bien tester leur présence sur la CG, utiliser glGetBooleanv(GL_TEXTURE_RECTANGLE_ARB, &rect_supported);
Plus important ( vital ! )
Les coordonnées de textures ne sont pas normalisées de 0 à 1, mais de 0 à height et de 0 à width.
Pensez-y !

Enfin, c'est une très mauvaise idée d'abandonner subitement les POTS. Elles sont énormément plus rapides, et bénéficient en plus du mipmapping, le gain de vitesse est monstueux. Les NPOTS sont à réserver aux cas particuliers.

Voilà, il n'y a plus grand chose à dire pour une utilisation conventionnelle des textures rectangulaires. Mais il y a bien d'autres surprises, par exemple si vous voulez les utilisez pour les shaders.

Les Frambuffer Object

Il est IMPERATIF de tester si la carte supporte cette extension. Même si en 2011, les cartes qui ne la supportent pas sont tellement vieilles que vous feriez mieux d'abandonner tout support technique pour les dinosaures qui en auraient encore une.

Le test est relativement simple:

GLenum status;
status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
status==GL_FRAMEBUFFER_COMPLETE_EXT?
	std::cout << "fbo supporté\n":
	std::cout << "fbo non supporté\n";

(N'oubliez pas d'initialiser glew au préalable)
Si c'est bon, on peut alors en créer un.

GLuint fbo;
glGenFramebuffersEXT(1, &amp;fbo);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,GL_TEXTURE_RECTANGLE_ARB, rect_tex, 0);

Vous remarquerez que, là encore, l'accès aux fonctionnalités d'openGL se fait via des integers, exactement de la même manière que les textures.
C'est peut-être un peu abstrait à manipuler au départ, mais je vous assure que l'on s'y fait vite.
D'ailleurs l'API elle-même est très similaire: glGenFrameBufferEXT, glBindFramebufferEXT... mis à part le Framebuffer et le EXT indiquant que cela ne fait pas partie de l'openGL de base, on ne se sent pas perdus.
La dernière ligne sert à dire que je dessinerai dans le framebuffer, ce sera en réalité dans la texture spécifiée.
Le premier paramètre est obligatoirement GL_FRAMEBUFFER_EXT, aussi stupide que cela _puisse_ paraître.
Le deuxième est GL_DEPTH_ATTACHMENT_EXT pour avoir accès au depth buffer, ou GL_COLOR_ATTACHMENT0_EXT pour la couleur normale.
le 3ème est le type de texture, GL_TEXTURE_2D ou GL_TEXTURE_RECTANGLE_ARB.
Le 4ème l'indice de la texture à utiliser.
Le dernier, enfin, détermine le niveau de mipmap dans lequel effectuer le dessin. Comme les textures rectangulaires ne permettent pas le mipmapping, ce sera forcément 0.

On va mainenant dessiner dans le fbo.

	glEnable(GL_TEXTURE_RECTANGLE_ARB);
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
	glDisable(GL_TEXTURE_RECTANGLE_ARB);
	glEnable(GL_TEXTURE_2D);
	// Dessin

On remarque qu'il est nécessaire que le type de texture utilisé par le fbo soit activé durant sa propre activation, mais après, on n'en a plus besoin. Il est même important de désactiver les textures rectangulaires, car sinon, comme précisé plus haut, ce paramètre aura précédence sur gl_texture_2d.

On va maintenant dessiner sur l'écran, avec notre texture toute neuve

	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // cela dessine dans le buffer normal, c'est à dire l'écran.
	// Un dessin...
	glBindTexture(GL_TEXTURE_RECTANGLE_ARB,rect_tex);
	// un autre dessin, texturé avec cette texture-ci.
	// rappellez vous que les bornes sont [0..w] et [0..h] et non [0..1] !

Voilà ! rien de plus simple, n'est-ce pas ? :)
N'oubliez pas de fermer la porte en partant : glDeleteFramebuffersEXT(1, &fbo);
Nous sommes entre gentlemen, après tout.

Conclusion

Voici donc une autre méthode pour dessiner dans une texture. J'ai ai profité pour présenter ici les textures rectangulaires, car la plupart des effets nécessitant cette méthode ( ou une équivalente ) sont utilisés pour afficher cette texture en plein écran. Une POT n'est donc pas très appropriée.

  1. No comments yet.
(will not be published)

  1. No trackbacks yet.