Jaak
Jaak

Reputation: 177

Back buffer contents into a texture in OpenGL

To make my maze type game faster I decided to put my drawed ball inside a texture, because i have to draw it otherwise once for every room and I'm drawing it like a concave polygon using the stencil buffer, it takes more time than using a texture. The problem is, that I'm getting it inside a texture correctly from the back buffer when I'm rendering the third frame since the start of the game and my question is, why is it like so?

When I'm using a texture from the thirst frame, I'm having texture with solid white color, so it has nothing inside. When I'm using textures from the second frame, then I have only the black background of the desired texture and when I take the texture from the third frame, then I have desired texture. For frame count I use the static variable "done" inside the "drawTexture" function.

Copying from the first frame:

enter image description here

Copying from the second frame:

enter image description here

Copying from the third frame (desired outcome):

enter image description here

void DrawBall::drawTexture(float imageD) {
    static int done = 0;

    if (done < 3) {
        drawToTexture(imageD);
        done++;
    }

    glEnable(GL_TEXTURE_2D);
    glBindTexture (GL_TEXTURE_2D, texture);

    glColor3f(1, 1, 1);
    glBegin (GL_QUADS);
        glTexCoord2f (0.0, 0.0); glVertex3f (0.0, 0.0, -imageD);    
        glTexCoord2f (1.0, 0.0); glVertex3f (5.0, 0.0, -imageD);
        glTexCoord2f (1.0, 1.0); glVertex3f (5.0, 5.0, -imageD);    
        glTexCoord2f (0.0, 1.0); glVertex3f (0.0, 5.0, -imageD);
    glEnd ();

    glDisable(GL_TEXTURE_2D);   
}

void DrawBall::drawToTexture(float imageD) {
    int viewport[4];
    glGetIntegerv(GL_VIEWPORT, (int*) viewport);

    int textureWidth = 64;
    int textureHeight = 64;

    texture = genEmptyTexture(textureWidth, textureHeight);

    glViewport(0, 0, textureWidth, textureHeight);
    glMatrixMode(GL_PROJECTION);                            
    glLoadIdentity();                                       
    gluPerspective(45.0f, 1, 1, 100);
    glMatrixMode(GL_MODELVIEW);                         
    glLoadIdentity();                                       

    /* 
       This function calculates the vertexes for the ball
       inside a vector<vector<float>> variable "test"
    */
    _calculateCircleVertexes(0.0f, 0.0f, -2.0f, 0.249f, &test, 20);

    _displayBall(&test, 0.0f, 0.0f, 0.5f, -2.0f, &*smallBallColor);

    glBindTexture(GL_TEXTURE_2D, texture);
    glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, textureWidth, textureHeight, 0);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
    glMatrixMode(GL_PROJECTION);    
    glLoadIdentity();               
    gluPerspective(45.0f, (GLfloat)viewport[2] / (GLfloat)viewport[3], 1.0f, imageD + 10.0f);
    glMatrixMode(GL_MODELVIEW);     
    glLoadIdentity();   
}

GLuint DrawBall::genEmptyTexture(unsigned int width, unsigned int height) {
    GLuint txtIndex;

    glGenTextures(1, &txtIndex);                                    
    glBindTexture(GL_TEXTURE_2D, txtIndex);                         

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,
    GL_RGB, GL_UNSIGNED_BYTE, NULL);                            

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

    return txtIndex;
}

void DrawBall::_displayBall(vector<vector<GLfloat>> *vertexes, GLfloat x, GLfloat y
                        , GLfloat imageW, GLfloat imageD, color *color) {
    glTranslatef(x, y, imageD);

    glClearStencil(0);
    glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
    glEnable(GL_STENCIL_TEST);
    glStencilFunc(GL_NEVER, 0, 1);
    glStencilOp(GL_INVERT, GL_INVERT, GL_INVERT);

    glBegin(GL_POLYGON);
        vector<vector<GLfloat>>::iterator it = vertexes->begin();
        for (; it != vertexes->end(); it++) {
            glVertex3f((*it)[0], (*it)[1], 0.0f);
        }
    glEnd();

    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    glStencilFunc(GL_EQUAL, 1, 1);
    glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);

    glColor3f(color->r, color->g, color->b);
    glBegin(GL_QUADS);
        glVertex3f(-(imageW / 2.0f), -(imageW / 2.0f), 0.0f);
        glVertex3f( (imageW / 2.0f), -(imageW / 2.0f), 0.0f);
        glVertex3f( (imageW / 2.0f),  (imageW / 2.0f), 0.0f);
        glVertex3f(-(imageW / 2.0f),  (imageW / 2.0f), 0.0f);
    glEnd();

    glDisable(GL_STENCIL_TEST);

    glTranslatef(x, y, -imageD);
}

Upvotes: 1

Views: 1897

Answers (2)

Jaak
Jaak

Reputation: 177

Well, Datenwolf, thank you for your suggestion, you are probably right but I just want to use the advanced stuff as less as possible and I found my mistakes. I didn't get the desired outcome before the second frame because I didn't have yet enabled stencil test. Before the first frame I didn't get the desired outcome because in the window creation Windows sends WM_SIZE message and I had the draw message inside it but at that time the OpenGL isn't set up properly yet.

Upvotes: 0

datenwolf
datenwolf

Reputation: 162297

You should not use the window framebuffer (which includes both back- and frontbuffer) for render to texture operations. It just breaks to easily (you've experienced it). Instead use a so called Framebuffer Object, with the texture as rendering target.

Upvotes: 2

Related Questions