Mipmapping not working with FBO ? Why ?

Started by
21 comments, last by Bakura 15 years, 10 months ago
Hi again !

I contacted Humus because I think he works at ATi in order to know :). Secondly, I've tried with UNSIGNED_BYTE textures RGBA, so there is no reason not to work... but it doesn't.

This framebuffer extension has managed to make me angry and want to break everything, which is a miracle !
Advertisement
OpenGL works like a charm for me and quite a few others I know. Can you do this without mipmapping first to see if that works... Try that first, and then move onto mipmapping. If you can't get the code to work without mipmapping something is seriously wrong.

BTW Humus doesn't work for ATI anymore, but he should know what to do.
Hi,

Without mipmapping it works well, even for GL_RGBA32F textures, with GL_NEAREST or GL_LINEAR. The problem is when I generate mipmap and try to render them... If my texture is 512*512 and all mipmaps generated, normally it should draw me the 256*256 texture if I draw a 256*256 quad (this is how it works when I generated them by myself in the fragment shader, but it's way too slow when you have several textures to generate...).

I think the best way to solve this problem is that I give you a minimal program that doesn't work, and then person who have ATi cards can test. I'm doing that... There I tried with GL_GENERATE_MIPMAP extension, but you can easily try with glGenerateMipmapEXT. Morever, as it uses SFML you may have to change the main (by using SDL or GLUT). The fragment shader just writes to the three textures (gl_FragData[0], gl_FragData[1], gl_FragData[2].

[source="cpp"]#include <iostream>#include <cstdlib>#include <GL/glew.h>#include <cmath>#include <SFML/Window.hpp>// Identifiant des vertex & fragment shaders, et des framebuffersGLuint lightPyramidsCreationVert;GLuint lightPyramidsCreationFrag;GLuint lightPyramidsCreationProg;GLuint lightPyramidsCreationFBO;GLuint lightPyramidsCreationPosition;GLuint lightPyramidsCreationNormal;GLuint lightPyramidsCreationIntensity;// Chargement d'un fichier texturechar* LoadSource(const char *filename){    char *src = NULL;   // code source de notre shader    FILE *fp = NULL;    // fichier    long size;          // taille du fichier    long i;             // compteur            // on ouvre le fichier    fp = fopen(filename, "r");    // on verifie si l'ouverture a echoue    if(fp == NULL)    {        fprintf(stderr, "impossible d'ouvrir le fichier '%s'\n", filename);        return NULL;    }        // on recupere la longueur du fichier    fseek(fp, 0, SEEK_END);    size = ftell(fp);      // on se replace au debut du fichier    rewind(fp);        // on alloue de la memoire pour y placer notre code source    src = (char*)malloc(size+1); // +1 pour le caractere de fin de chaine '\0'    if(src == NULL)    {        fclose(fp);        fprintf(stderr, "erreur d'allocation de memoire!\n");        return NULL;    }        // lecture du fichier    for(i=0; i<size; i++)        src = fgetc(fp);        // on place le dernier caractere a '\0'    src[size] = '\0';        fclose(fp);        return src;}void InitOGL (){	glewInit (); 	// Set color and depth clear value   glClearDepth(1.f);   glClearColor(0.f, 0.f, 0.3f, 1.f);    // Enable Z-buffer read and write   glEnable(GL_DEPTH_TEST);   glDepthMask(GL_TRUE);    // Setup a perspective projection   glMatrixMode(GL_PROJECTION);   glLoadIdentity(); 	glViewport (0, 0, 512, 512); // On règle le Viewport    glMatrixMode (GL_PROJECTION);   glLoadIdentity ();   gluPerspective (60.0, 512.0 / 512.0, 1.0, 400.0);    glMatrixMode (GL_MODELVIEW);   glLoadIdentity (); // On rétablit la matrice identité	// On charche, dans un premier temps, les shaders	lightPyramidsCreationVert = glCreateShader (GL_VERTEX_SHADER);   lightPyramidsCreationFrag = glCreateShader (GL_FRAGMENT_SHADER);	char * source3 = LoadSource ("LightPyramidsCreation.vert");	char * source4 = LoadSource ("LightPyramidsCreation.frag");	glShaderSource (lightPyramidsCreationVert, 1, (const GLchar **)&source3, NULL);	glShaderSource (lightPyramidsCreationFrag, 1, (const GLchar **)&source4, NULL);	glCompileShader (lightPyramidsCreationVert);	glCompileShader (lightPyramidsCreationFrag);	lightPyramidsCreationProg = glCreateProgram ();	glAttachShader (lightPyramidsCreationProg, lightPyramidsCreationVert);	glAttachShader (lightPyramidsCreationProg, lightPyramidsCreationFrag);	glLinkProgram (lightPyramidsCreationProg);	// On créé à présent le framebuffer, ainsi que les deux textures destinées à recevoir le tout	glGenFramebuffersEXT (1, &lightPyramidsCreationFBO);		glGenTextures (1, &lightPyramidsCreationPosition);	glBindTexture (GL_TEXTURE_2D, lightPyramidsCreationPosition);	glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, 512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);	glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);	glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);	glTexParameteri (GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);		glGenTextures (1, &lightPyramidsCreationNormal);	glBindTexture (GL_TEXTURE_2D, lightPyramidsCreationNormal);	glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, 512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);	glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);	glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);	glTexParameteri (GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);			glGenTextures (1, &lightPyramidsCreationIntensity);	glBindTexture (GL_TEXTURE_2D, lightPyramidsCreationIntensity);	glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, 512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);	glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);	glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);	glTexParameteri (GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);	glBindTexture (GL_TEXTURE_2D, 0);		glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, lightPyramidsCreationFBO);	glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, lightPyramidsCreationPosition, 0);	glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, lightPyramidsCreationNormal, 0);	glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT2_EXT, GL_TEXTURE_2D, lightPyramidsCreationIntensity, 0);	GLenum status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT);	if (status == GL_FRAMEBUFFER_COMPLETE_EXT)		std::cerr << "Creation du framebuffer parfaite" << std::endl;		glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);}void Destroy (){	glDeleteShader (lightPyramidsCreationVert);	glDeleteShader (lightPyramidsCreationFrag);	glDeleteProgram (lightPyramidsCreationProg);		glDeleteTextures (1, &lightPyramidsCreationPosition);	glDeleteTextures (1, &lightPyramidsCreationNormal);	glDeleteTextures (1, &lightPyramidsCreationIntensity);	glDeleteFramebuffersEXT (1, &lightPyramidsCreationFBO);}void RenderScene (){	// On dessine un sol	GLfloat gris[] = {0.8f, 0.8f, 0.8f};	glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, gris);	glBegin (GL_QUADS);		glNormal3f (0.0f, 1.0f, 0.0f);		glVertex3f (-5.0f, 0.0f, 5.0f);		glVertex3f (-5.0f, 0.0f, -5.0f);		glVertex3f (5.0f, 0.0f, -5.0f);		glVertex3f (5.0f, 0.0f, 5.0f);	glEnd ();	glBegin (GL_QUADS);		glNormal3f (0.0f, -1.0f, 0.0f);		glVertex3f (-5.0f, 10.0f, 5.0f);		glVertex3f (-5.0f, 10.0f, -5.0f);		glVertex3f (5.0f, 10.0f, -5.0f);		glVertex3f (5.0f, 10.0f, 5.0f);	glEnd ();	glBegin (GL_QUADS);		glNormal3f (0.0f, 1.0f, 1.0f);		glVertex3f (-5.0f, 0.0f, -5.0f);		glVertex3f (5.0f, 0.0f, -5.0f);		glVertex3f (5.0f, 10.0f, -5.0f);		glVertex3f (-5.0f, 10.0f, -5.0f);	glEnd ();	GLfloat rouge[] = {1.0f, 0.0f, 0.0f};	glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, rouge);	glBegin (GL_QUADS);		glNormal3f (1.0f, 0.0f, 0.0f);		glVertex3f (-5.0f, 0.0f, 5.0f);		glVertex3f (-5.0f, 0.0f, -5.0f);		glVertex3f (-5.0f, 10.0f, -5.0f);		glVertex3f (-5.0f, 10.0f, 5.0f);	glEnd ();	GLfloat vert[] = {0.0f, 1.0f, 0.0f};	glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, vert);	glBegin (GL_QUADS);		glNormal3f (-1.0f, 0.0f, 0.0f);		glVertex3f (5.0f, 0.0f, 5.0f);		glVertex3f (5.0f, 0.0f, -5.0f);		glVertex3f (5.0f, 10.0f, -5.0f);		glVertex3f (5.0f, 10.0f, 5.0f);	glEnd ();}void DrawQuad (float w, float h){	// On utilise une perception 2D	glMatrixMode (GL_PROJECTION);	glPushMatrix ();	glLoadIdentity ();		gluOrtho2D (0.0, 512.0, 0.0, 512.0);	glMatrixMode (GL_MODELVIEW);	glLoadIdentity ();		glBegin (GL_QUADS);		glTexCoord2f (0.0f, 0.0f);		glVertex2f (0.0f, 0.0f);		glTexCoord2f (1.0f, 0.0f);		glVertex2f (w, 0.0f);		glTexCoord2f (1.0f, 1.0f);		glVertex2f (w, h);		glTexCoord2f (0.0f, 1.0f);		glVertex2f (0.0f, h);	glEnd ();		glMatrixMode (GL_PROJECTION);	glPopMatrix ();}void Render (){	glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	glMatrixMode (GL_MODELVIEW);	glLoadIdentity ();	gluLookAt (0.0f, 0.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);	glTranslatef (0.0, -5.0, 0.0);		// ------------------------------------ PASSE 1 --------------------------------------	// On rend la scène dans le framebuffer	glUseProgram (lightPyramidsCreationProg);	glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, lightPyramidsCreationFBO);	glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);		GLenum buffers[] = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_COLOR_ATTACHMENT2_EXT};	glDrawBuffers (3, buffers);   RenderScene();	glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);	glUseProgram (0);			// On rend les textures à l'écran	glEnable (GL_TEXTURE_2D);	glBindTexture (GL_TEXTURE_2D, lightPyramidsCreationPosition);		DrawQuad (256.0, 256.0);	glBindTexture (GL_TEXTURE_2D, 0);	glDisable (GL_TEXTURE_2D);}int main(){    // Create the main window	sf::Window App(sf::VideoMode(512, 512, 32), "SFML OpenGL");    // Create a clock for measuring time elapsed    sf::Clock Clock;    InitOGL ();    // Start game loop    bool Running = true;    while (Running)    {        // Process events        sf::Event Event;        while (App.GetEvent(Event))        {            // Close window : exit            if (Event.Type == sf::Event::Closed)                Running = false;            // Escape key : exit            if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Escape))                Running = false;            // Resize event : adjust viewport            if (Event.Type == sf::Event::Resized)                glViewport(0, 0, Event.Size.Width, Event.Size.Height);        }        // Set the active window before using OpenGL commands        // It's useless here because active window is always the same,        // but don't forget it if you use multiple windows or controls        App.SetCurrent();        Render ();        // Finally, display rendered frame on screen        App.Display();    }	 Destroy ();    return EXIT_SUCCESS;}
YES !! I finally get it working !! After long hours of testing... As you'll see, the solution I've found is... awkward and completely unlogical, but since it works, I'll use it until I found better.

Here is how I have to do :

At Init time :

Create all the textures and the first level (set all the levels also work, but I don't think there is any difference). Set GL_LINEAR or GL_NEAREST to both GL_MIN or GL_MAX filtering mode. Setting a mipmap setting make it a black texture. SET GL_GENERATE_MIPMAP TO GL_TRUE.

At draw time :

Draw into the framebuffer, unbind the framebuffer. For each texture that was generated by the FBO, bind it, set the desired filtering mode (here I set GL_LINEAR_MIPMAP_NEAREST), AND CALL glGenerateMipmapEXT. Here, it works.


So yes, I have to use both extensions (GL_SGIS_GENERATE_MIPMAP) and the glGenerateMipmapEXT. And yes, I have to set a non mipmap filtering mode at setup, and I really don't know why, as after binding the texture, I set GL_LINEAR_MIPMAP_NEAREST, and never reset GL_LINEAR, so after the first frame the FBO will use textures with GL_LINEAR_MIPMAP_NEAREST as filtering...

Moreover, I don't know why I have to use both extensions, but it works, I now know it is supported by ATI cards. Finally, I don't know why I have to bind eeach texture and call glGenerateMipmap... I thought it was possible to generate each of them with one call. And here the result :

http://tof.canardplus.com/show/47aa57ed-9d8a-4c27-b618-535221c7cbf5.html (click here)
glGenerateMipmapEXT should work, makes me think that either its a driver issue, or you don't have a complete mipmap chain setup, that is why the glTexParameteri (GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); works. The glGenerateMipmapEXT() doesn't create the mipmaps for you it only updates the chain you have created, IIRC. Either way glad its working for you.

Hi,

I'm glad too, but when I create the whole chain by myself and call glGenerateMipmap, it doesn't work. I'll wait three or four drivers and try again the normal way ;).
Hi,

Some news from here. Everything got working well, except that I found a subtle bug. In fact, the sublevels were correctly generated, but they were never updated. That is to say that although the base level is updated, all the other sublevels aren't. The only solution I found to update the subbuffers was to reset the texture data of one of the levels (each level work, but deleting the last level - 1*1 texture - is much more faster than deleting the second level).

So, here is how it works well :

1) Create texture (base level), and set GL_LINEAR or GL_NEAREST filters
2) Set GL_GENERATE_MIPMAP to GL_TRUE at init time
3) Draw to the texture via the framebuffer, as usual
4) Bind the texture, call glTexImage2D (GL_TEXTURE_2D, 9, GL_RGBA16F_ARB, 1, 1, 0, GL_RGBA, GL_FLOAT, NULL) for exemple, set GL_LINEAR_MIPMAP_NEAREST filter, and call glGenerateMipmapEXT (GL_TEXTURE_2D), and we're done ! It works

The strange things is that according to the GL_GENERATE_MIPMAP extension spec, only the sublevels are updated, so if I update level 8, only level 9 should be updated, but here, if I update level 9 (1*1 texture), all the levels are updated.

Quite strange :D.
According to the GL 2.1 specification, when you make a change to the base level, a complete set of mipmaps are generated. It does not say what should happen when you update a mipmap. This is on p178 Automatic Mipmap Generation.

In principle, you shouldn't use this along with FBO. You should only use glGenerateMipmapsEXT although it doesn't say it anywhere in the spec. I'm just basing this on the fact that glGenerateMipmapsEXT when FBOs were introduced.
Sig: http://glhlib.sourceforge.net
an open source GLU replacement library. Much more modern than GLU.
float matrix[16], inverse_matrix[16];
glhLoadIdentityf2(matrix);
glhTranslatef2(matrix, 0.0, 0.0, 5.0);
glhRotateAboutXf2(matrix, angleInRadians);
glhScalef2(matrix, 1.0, 1.0, -1.0);
glhQuickInvertMatrixf2(matrix, inverse_matrix);
glUniformMatrix4fv(uniformLocation1, 1, FALSE, matrix);
glUniformMatrix4fv(uniformLocation2, 1, FALSE, inverse_matrix);
I would love to only use glGenerateMipmap, as my implementation is really strange, but the problem is that it doesn't work... If somebody could make it working on a ATi card (I've got a 3870 HD) with a minimal code, I would be glad to have to whole source, but I assure you, I've tried everything possible, but I HAVE TO use glGenerateMipmap AND GL_GENERATE_MIPMAP AND reset one of the level. For sure, this is not normal (and certainly not optimal), but this is the only solution that works so far.

I really hope that OGL3.0 will have better support on ATi cards...
I would have to say that sucks. Perhaps you can make an example and send to their feedback department. I think it is devrel@ati.com
Sig: http://glhlib.sourceforge.net
an open source GLU replacement library. Much more modern than GLU.
float matrix[16], inverse_matrix[16];
glhLoadIdentityf2(matrix);
glhTranslatef2(matrix, 0.0, 0.0, 5.0);
glhRotateAboutXf2(matrix, angleInRadians);
glhScalef2(matrix, 1.0, 1.0, -1.0);
glhQuickInvertMatrixf2(matrix, inverse_matrix);
glUniformMatrix4fv(uniformLocation1, 1, FALSE, matrix);
glUniformMatrix4fv(uniformLocation2, 1, FALSE, inverse_matrix);

This topic is closed to new replies.

Advertisement