Search code examples
openglframebufferfbo

OpenGL: Rendering to texture by using FBO and viewport offset problems


I have noticed unexpected behavior of frame-buffer objects (FBO) when rendering to a texture.

If we set viewport in following way:

glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, 1.0, 1.0, 0.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity() ;

(w and h doesn't need to match window size) everything is rendered fine:

So lets say we need to draw bounding rectangle of viewport:

glBegin(GL_LINE_STRIP);
    glVertex2f(0.0, 0.0);
    glVertex2f(0.0, 1.0);
    glVertex2f(1.0, 1.0);
    glVertex2f(1.0, 0.0);
glEnd();

If we do the same drawing on a texture then covering whole viewport:

glBegin(GL_QUADS);
    glTexCoord2f(0.0, 1.0);
    glVertex2f(0.0, 0.0);
    glTexCoord2f(0.0, 0.0);
    glVertex2f(0.0, 1.0);
    glTexCoord2f(1.0, 0.0);
    glVertex2f(1.0, 1.0);
    glTexCoord2f(1.0, 1.0);
    glVertex2f(1.0, 0.0);
glEnd();

We get same results:

But, if we change first line to:

glViewport(x, y, w, h);

where x and y are NOT equal 0 - there is a strange offset when doing FBO version. Offset is exactly equal to (x, y) but it is relative to a viewport so we get resulting offset off (2*x, 2*y)

Here is full code demonstrating problem.

void initialize()
{
    glClearColor(0.0, 0.0, 0.0, 0.0);
}

void resize(int w, int h)
{
    glViewport(50, 50, w-100, h-100);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    gluOrtho2D(0.0, 1.0, 1.0, 0.0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity() ;
}

void paint()
{
    glClear(GL_COLOR_BUFFER_BIT);

    // FBO Drawing
    GLuint fb, texColor;
    glGenFramebuffers(1, &fb);
    glGenTextures(1, &texColor);

    glBindFramebuffer(GL_FRAMEBUFFER, fb);
    glBindTexture(GL_TEXTURE_2D, texColor);
    glTexImage2D(   GL_TEXTURE_2D,
                0,
                GL_RGBA,
                width()-100, height()-100, // Maching size of viewport (size of window - unused area)
                0,
                GL_RGBA,
                GL_UNSIGNED_BYTE,
                NULL);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texColor, 0);

    glLineWidth(10.0);
    glColor4f(1.0, 0.0, 0.0, 1.0);
    glBegin(GL_LINE_STRIP);
        glVertex2f(0.0, 0.0);
        glVertex2f(0.0, 1.0);
        glVertex2f(1.0, 1.0);
        glVertex2f(1.0, 0.0);
    glEnd();
    glBegin(GL_LINES);
        glVertex2f(0.0, 0.0);
        glVertex2f(1.0, 1.0);
        glVertex2f(0.0, 1.0);
        glVertex2f(1.0, 0.0);
    glEnd();


    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);

    glLineWidth(10.0);
    glColor4f(0.0, 1.0, 0.0, 1.0);
    glBegin(GL_LINE_STRIP);
        glVertex2f(0.0, 0.0);
        glVertex2f(0.0, 1.0);
        glVertex2f(1.0, 1.0);
        glVertex2f(1.0, 0.0);
    glEnd();
    glBegin(GL_LINES);
        glVertex2f(0.0, 0.0);
        glVertex2f(1.0, 1.0);
        glVertex2f(0.0, 1.0);
        glVertex2f(1.0, 0.0);
    glEnd();

    glEnable(GL_TEXTURE_2D);

    glBindTexture(GL_TEXTURE_2D, texColor);
    glColor4f(1.0, 1.0, 1.0, 0.3);
    glBegin(GL_QUADS);
        glTexCoord2f(0.0, 1.0);
        glVertex2f(0.0, 0.0);
        glTexCoord2f(0.0, 0.0);
        glVertex2f(0.0, 1.0);
        glTexCoord2f(1.0, 0.0);
        glVertex2f(1.0, 1.0);
        glTexCoord2f(1.0, 1.0);
        glVertex2f(1.0, 0.0);
    glEnd();

    glDisable(GL_TEXTURE_2D);
    glDisable(GL_BLEND);

    glDeleteTextures(1, &texColor);
    glDeleteFramebuffers(1, &fb);

Why green and red drawing do not match?

Screenshot


Solution

  • You of course get the twice the viewport - since you basically apply it twice - the viewport is also effective when rendring to the texture, so you end up with the object origin mappted to pixel (x,y) in the texture. Then, you draw that texture, again with the viewport, so the quad begins at (x,y) in window coords, and the object in the texture is further shifted away - the two offsets accumulate.

    Just set the viewport to begin at origin when you render into the texture.