Problem with buffers (OpenGL ES)

This is a question for OpenGL ES on the iPhone, but the functionality should all be the same in the end - pretty much I am drawing a dialog box, and having each letter rendered individually every frame is way way too expensive. I want to draw all the letters into a framebuffer, have that generate a texture once, and then only draw that generated texture.

I’ve gotten nothing but a white box so far, unfortunately. I’m probably doing something wrong in binding the texture and setting the min filter, but I’m not sure. I’m not getting any help on StackOverflow, so I thought I’d ask around here too.

I’ve tried to convert it mostly to Java for you guys, although I haven’t made the GL calls object oriented - I assume you’ll be able to understand what I’m doing regardless. Also GLuint and all that can just be interpreted as their Java counterparts (int, float, etc.). Any calls to Globals or EAGLView are pretty straightforward and can more or less be ignored. I already know that most of this code works (it is duplicated from other working pieces) - the only new bit here is dynamically generating a texture and using a framebuffer.


//Declare instance variables.
GLuint texture;
GLuint textureFrameBuffer;


//Generate the texture and framebuffer.
        glGetError();

        //Generate the texture that we will draw to (saves us a lot of processor).
        glEnable(GL_TEXTURE_2D);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

        // Use OpenGL ES to generate a name for the texture.
        // Pass by reference so that our texture variable gets set.
        glGenTextures(1, &texture);

        // Bind the texture name. 
        glBindTexture(GL_TEXTURE_2D, texture);

        // Specify a 2D texture image, providing a pointer to the image data in memory.
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 512, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);

        //Create a frame buffer to draw to. This will allow us to directly edit the texture.
        GLint oldFBO;
        glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, &oldFBO);
        glGenFramebuffersOES(1, &textureFrameBuffer);
        glBindFramebufferOES(GL_FRAMEBUFFER_OES, textureFrameBuffer);
        glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, texture, 0);

        glBindFramebufferOES(GL_FRAMEBUFFER_OES, oldFBO);

        GLenum err = glGetError();
        if (err != GL_NO_ERROR)
        {
                System.out.println("Error on framebuffer init. glError: " + err);
        }


//Draw a big string into the framebuffer.

glGetError();

GLint oldFBO;
glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, &oldFBO);

//Bind our frame buffer.
glBindFramebufferOES(GL_FRAMEBUFFER_OES, textureFrameBuffer);

//Clear out the texture.
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

//Draw the letters to the frame buffer. (calls glDrawArrays a bunch of times, binds various textures, etc.) Does everything in 2D.
renderDialog(displayString, fontAtlas, fontSpriteName);

//Unbind the frame buffer.
glBindFramebufferOES(GL_FRAMEBUFFER_OES, oldFBO);

GLenum err = glGetError();
if (err != GL_NO_ERROR)
{
        System.out.println("Error on string creation. glError: " + err);
}


//Draw it.

        glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

        glGetError();

        //Draw the text.
        EAGLView.enable2D();

        //Push the matrix so we can keep it as it was previously.
        glPushMatrix();

        //Store the coordinates/dimensions from the rectangle.
        float x = 0;
        float y = Globals.getPlayableHeight() - dialogRect.size.height;
        float w = Globals getPlayableWidth();
        float h = dialogRect.size.height;

        // Set up an array of values to use as the sprite vertices.
        GLfloat vertices[] =
        {
                x,              y,
                x,              y+h,
                x+w,    y+h,
                x+w,    y
        };

        // Set up an array of values for the texture coordinates.
        GLfloat texcoords[] =
        {
                0,                      0,
                0,                      h / 128,
                w / 512,        h / 128,
                w / 512,        0
        };

        //Render the vertices by pointing to the arrays.
        glVertexPointer(2, GL_FLOAT, 0, vertices);
        glTexCoordPointer(2, GL_FLOAT, 0, texcoords);

        // Set the texture parameters to use a linear filter when minifying.
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_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);

        //Allow transparency and blending.
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        //Enable 2D textures.
        glEnable(GL_TEXTURE_2D);

        //Bind this texture.
        EAGLView.bindTexture(texture);

        //Finally draw the arrays.
        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

        //Restore the model view matrix to prevent contamination.
        glPopMatrix();

        GLenum err = glGetError();
        if (err != GL_NO_ERROR)
        {
                System.out.println("Error on draw. glError: ", err);
        }

Any help would be thoroughly appreciated.

I have a couple of guesses, or things that you should do:

  1. You might need to attach a depth renderbuffer if you’re not attaching a depth texture in order to make the fbo complete.
  2. You should check for fbo completeness (here I’m assuming that the ES fbos have approximately the same rules that desktop ones do):

int complete = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (complete != GL_FRAMEBUFFER_COMPLETE) {
 // complete could be one of:
 // GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT
 // GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
 // GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER
 // GL_FRAMEBUFFER_IMCOMPLETE_READ_BUFFER
 // GL_FRAMEBUFFER_UNSUPPORTED
}

  1. Make sure you’ve configured the correct buffers for drawing and reading, using the calls glDrawBuffers() and glReadBuffers.

Usually what I do is create an int[] array that has elements equal to the color attachments that I’m using for the fbo (e.g. GL_COLOR_ATTACHMENT0 + i, for some i), and then call


glDrawBuffers(array.length, array)

HTH (all of this feedback is given with the assumption that the rendering isn’t working because the fbo isn’t setup properly).

Well, I’ve been tweaking it now and I had a totally moron mistake that had nothing to do with FBOs (I was checking to see if an unsigned int was less than zero before I initialized the FBO; naturally it will never be less than zero), and now I’m getting a different problem.

  • The FBO is considered complete upon creation.
  • I still haven’t tried a depth buffer, although I am only drawing in 2D with depth testing off so I don’t think this should be a problem.
  • I am not drawing the buffer using glDrawBuffers; instead I am just trying to draw the texture that gets created via the FBO.

Instead of a white box, I now get a white box on the left and a black one on the right. These boxes correspond to two textures I am attempting to draw:

  • glColor4f(1.0f, 1.0f, 1.0f, 1.0f) is set before either texture is drawn.
  • One is drawn on the left side of the FBO, it is rendered white. If I change the glColor, it matches the color that I changed it to.
  • The other is drawn on the right side of the FBO. It always appears black for some reason.
  • The letters drawn on top of the black area do not appear (probably they are drawn black also).

In fact, after further tweaking, although stuff is drawn in the wrong places (I think I messed up glViewport), I can now see several white squares being drawn as well, presumedly these are the letters.

So it looks like the FBO gets drawn correctly in the end, as well as created correctly, but when I draw into it I’m having problems (I am using the same drawing methods I use to draw elsewhere, and these have no problems). Think I’m missing a filter call or something so all these textures are not being fully bound?

I think I’m having a little trouble understanding what you’re trying to draw. Here is how I think it sounds:

  1. Render two rectangles into the bound fbo, the left one matches the set color and the right one is always black.
  2. The fbo is then drawn onto the screen using its texture and a fullscreen quad
  3. Some characters should have been rendered into the fbo (and thus be visible after #2), but the characters don’t show up, only smaller white squares that could be them.

This really sounds like a silly mistake is lurking somewhere. I’d check that you’re using the fbo’s dimensions when setting up its projections. Also, you could try rendering something into the fbo that isn’t as complicated as character data just to make sure that side of things is okay.

I’m pretty sure you’re not doing this, but this line made me leery:

You’re not using the fbo’s texture while the fbo is bound?

I’m guessing you’re right about the silly mistake. I made an empty project and I’m just trying to get FBOs in that working first, then I’m going to port it over to my other project. It’s possible something in the mix is messing up OpenGL’s state, also.

I don’t think I’m using the texture while the FBO is bound. I bind it, bind the texture to it, draw to it, then unbind it. Later (in a totally separate loop iteration) I draw the texture, but I never bind the FBO again after its initial creation.

Hmm, good luck I hope the clean project shows you the way.

All right, the clean project did indeed show me my problem (for whatever reason, if I generated the FBO and then immediately drew to it, it would break. The solution was to generate the FBO outside of the render loop, then inside the render loop draw to the FBO - weird!).

But now I have another problem, and it’s really really weird.

Here are the steps of what I do:

  1. Initialize GL params.
  2. Create FBO, using an empty 512x512 texture as the framebuffer texture.
  3. Start draw loop.
  4. Draw my 128x128 sprite to the FBO at 320x480 (iPhone screen dimensions). Only do this on the first render call.
  5. Draw my FBO texture to the screen at 320x480.

The results? Very weird. It seems that the FBO gets drawn twice, once rotated 90 degrees. If I use the 320x480, it will fit perfectly, so I will only see bad Z-fighting. If I draw the sprite into the FBO at something like 100x200, then I can clearly see that it is rotated from the origin.

Any ideas here?

Original image:

http://img710.imageshack.us/img710/1518/testsprite.png

Drawn at 320x480 (note the Z-fighting above the numbers)

http://img141.imageshack.us/img141/5666/screenshot20100106at235.png

Drawn at 100x300 (check out the weird mirroring!)

http://img130.imageshack.us/img130/4404/screenshot20100106at236.png

Also, this StackOverflow post I just made has all the most recent source code:

AHAHAHAHAHAHA
AHAHAHAHAHAHA
AHAHAHAHAHAHA
AHAHAHAHAHAHA

I flipped a 0 and 1. In my texture coordinates. I should have known.

Sometimes it’s good to be retarded!

It definitely makes the fixes easier :slight_smile: