OpenGL:使用 FBO 和视口偏移问题渲染到纹理

Posted

技术标签:

【中文标题】OpenGL:使用 FBO 和视口偏移问题渲染到纹理【英文标题】:OpenGL: Rendering to texture by using FBO and viewport offset problems 【发布时间】:2014-02-27 15:12:36 【问题描述】:

我注意到渲染到纹理时帧缓冲区对象 (FBO) 的意外行为。

如果我们按以下方式设置视口:

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

(w和h不需要匹配窗口大小)一切都很好:

假设我们需要绘制视口的边界矩形:

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_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();

我们得到相同的结果:

但是,如果我们将第一行改为:

glViewport(x, y, w, h);

其中 x 和 y 不等于 0 - 执行 FBO 版本时有一个奇怪的偏移量。偏移量完全等于 (x, y) 但它是相对于视口的,所以我们得到偏移量 (2*x, 2*y)

这是演示问题的完整代码。

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);

为什么绿色和红色绘图不匹配?

【问题讨论】:

【参考方案1】:

您当然会获得两倍的视口 - 因为您基本上应用了两次 - 视口在渲染到纹理时也是有效的,因此您最终会将对象原点映射到纹理中的像素 (x,y)。然后,再次使用视口绘制该纹理,因此四边形在窗口坐标中的 (x,y) 处开始,并且纹理中的对象进一步移开 - 两个偏移量累积。

当您渲染到纹理中时,只需将视口设置为从原点开始。

【讨论】:

哇!我刚刚看到并开始写相同的答案。折断!这是正确的答案。 这是有道理的。当我做一些测试时,我会将此标记为正确答案。另一件事,由于视口对所有 FBO 都是全局的,我如何仅针对纹理更改它?如果没有,我是否需要创建屏幕大小的纹理(不是视口)才能正确渲染? @PredragManojlovic:GL 是一个状态机 - 视口将保持这种状态,直到您将其设置为其他内容。只需在渲染到 FBO 之前设置视口,并在渲染到窗口时设置其他视口。 就是这样!正确答案!【参考方案2】:

如果 width() == wheight() == hwh 值在您的 resize 函数中),那么您的纹理(和 FBO)大小与您的视口大小不同。明确地说,你的视口是(w-100) * (h-100),你的纹理是(w-150) * (h-150)

【讨论】:

你是对的。纠正问题描述。但不幸的是,这不是原因,因为修正后问题仍然存在

以上是关于OpenGL:使用 FBO 和视口偏移问题渲染到纹理的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL 渲染到 FBO

使用 FBO 和着色器 OpenGL 渲染到纹理

OpenGL:使用多个纹理渲染到 FBO

使用带有 FBO 的 OpenGL 3.2+ 的 Linux 屏幕外渲染

OpenGL - 在 NVIDIA 卡上渲染到纹理时出现 FBO 黑屏

在 Android 上使用 FBO 进行慢速 OpenGL ES 渲染到纹理乒乓球