【问题标题】:OpenGL: Trouble with Render to Texture & Framebuffer ObjectOpenGL:渲染到纹理和帧缓冲区对象的问题
【发布时间】:2011-05-14 23:12:40
【问题描述】:

我想将场景渲染为最初为空的纹理。为此,我使用了一个帧缓冲区对象,我将一个空的 2d 纹理和一个深度缓冲区附加到该对象上。设置完成后,至于测试,我在场景中绘制了一个简单的四边形。每个顶点都有不同的颜色,所以我最终希望纹理中有一个颜色插值的四边形。 然后我使用包含四边形的纹理并将其映射到另一个四边形上。所以最终,我的默认帧缓冲区中有一个四边形,它的纹理包含一个彩色四边形。我希望这不会太混乱......

无论如何,我一定是在这里遗漏了一些东西,因为我得到的只是灰色的纹理。我基本上遵循了this 非常简单的说明。但是,我无法完全弄清楚我在这里缺少什么。如果有人能给我一个线索,我将不胜感激。

谢谢 沃尔特


这是我到目前为止的代码: // 创建帧缓冲对象 glGenFramebuffers(1, &frameBufferObject);

// create depth buffer
glGenRenderbuffers(1, &depthAttachment);

// create empty texture
int width = 512;
int height = 512;
int numberOfChannels = 3;
GLuint internalFormat = GL_RGB;
GLuint format = GL_RGB;

unsigned char* texels = new unsigned char[width * height * numberOfChannels];

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

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

glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, texels);

glGenerateMipmap(GL_TEXTURE_2D);

delete[] texels;
texels = NULL;

// activate & bind empty texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);

// attach empty texture to framebuffer object
glBindFramebuffer(GL_FRAMEBUFFER, frameBufferObject);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);

// define render targets (empty texture is at GL_COLOR_ATTACHMENT0)
glDrawBuffers(1, GL_COLOR_ATTACHMENT0);

// attach depth buffer
glBindRenderbuffer(GL_RENDERBUFFER, depthAttachment);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthAttachment);

// use framebuffer object
glBindFramebuffer(GL_FRAMEBUFFER, frameBufferObject);

// draw the colored quad into the initially empty texture
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);

// store attibutes
glPushAttrib(GL_VIEWPORT_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// reset viewport
glViewport(0, 0, width, height);

// make background yellow
glClearColor(1.0f, 1.0f, 0.0f, 1.0f);

// draw quad into texture attached to frame buffer object
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glBegin(GL_QUADS);
    glColor4f(1.0f, 1.0f, 1.0f, 1.0f); glVertex2f(0.0f, 100.0f); // top left
    glColor4f(1.0f, 0.0f, 0.0f, 1.0f); glVertex2f(0.0f, 0.0f); // bottom left
    glColor4f(0.0f, 1.0f, 0.0f, 1.0f); glVertex2f(100.0f, 0.0f); // bottom right
    glColor4f(0.0f, 0.0f, 1.0f, 1.0f); glVertex2f(100.0f, 100.0f); // top right
glEnd();

// restore attributes
glPopAttrib();

glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);

// use default framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);

// clear default framebuffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// draw the scene
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();

glColor4f(1.0, 1.0, 1.0, 1.0);

// begin texture mapping
glEnable(GL_TEXTURE_2D);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

glBegin(GL_QUADS);
    glNormal3d(0.0f, 0.0f, 1.0f);
    glTexCoord2f(0.0f, 0.0f); glVertex3f(0.0f, 50.0f, -100.0f);    // top left
    glTexCoord2f(0.0f, 1.0f); glVertex3f(0.0f, 0.0f, -100.0f);     // bottom left
    glTexCoord2f(1.0f, 1.0f); glVertex3f(50.0f, 0.0f, -100.0f);    // bottom right
    glTexCoord2f(1.0f, 0.0f); glVertex3f(50.0f, 50.0f, -100.0f);   // top right
glEnd();

glDisable(GL_TEXTURE_2D);

glPopMatrix();

// swap buffers (I forgot to mention that I use SDL)
SDL_GL_SwapBuffers();

预期结果:

  • 黄色背景上带有彩色四边形的纹理
  • 该纹理映射到另一个四边形

实际结果:

  • 灰色纹理
  • 纹理可以成功映射到另一个四边形

编辑:我没有提到错误处理。这就是我检查错误的方式。它包括 Paul S 建议的其他案例。谢谢。

GLuint status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
switch(status) {
    case GL_FRAMEBUFFER_COMPLETE:
        return;
        break;

case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
    throw FramebufferIncompleteException("An attachment could not be bound to frame buffer object!");
    break;

case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
    throw FramebufferIncompleteException("Attachments are missing! At least one image (texture) must be bound to the frame buffer object!");
    break;

case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
    throw FramebufferIncompleteException("The dimensions of the buffers attached to the currently used frame buffer object do not match!");
    break;

case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
    throw FramebufferIncompleteException("The formats of the currently used frame buffer object are not supported or do not fit together!");
    break;

case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
    throw FramebufferIncompleteException("A Draw buffer is incomplete or undefinied. All draw buffers must specify attachment points that have images attached.");
    break;

case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
    throw FramebufferIncompleteException("A Read buffer is incomplete or undefinied. All read buffers must specify attachment points that have images attached.");
    break;

case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
    throw FramebufferIncompleteException("All images must have the same number of multisample samples.");
    break;

case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS :
    throw FramebufferIncompleteException("If a layered image is attached to one attachment, then all attachments must be layered attachments. The attached layers do not have to have the same number of layers, nor do the layers have to come from the same kind of texture.");
    break;

case GL_FRAMEBUFFER_UNSUPPORTED:
    throw FramebufferIncompleteException("Attempt to use an unsupported format combinaton!");
    break;

default:
    throw FramebufferIncompleteException("Unknown error while attempting to create frame buffer object!");
    break;
}

编辑 2:这是我用来检查 GL 错误的方法

checkForGLErrors(string sourceFile, int line)
    GLenum error = glGetError();
    ostringstream o;

    switch(error) {
        case GL_NO_ERROR:
            return;
            break;

        case GL_INVALID_ENUM:
            o<<"OpenGL Error in "<<sourceFile<<" at line "<<line<<": Invalid enum!"<<endl;
            throw GLErrorException(o.str());
            break;

        case GL_INVALID_VALUE:
            o<<"OpenGL Error in "<<sourceFile<<" at line "<<line<<": Invalid value!"<<endl;
            throw GLErrorException(o.str());
            break;

        case GL_INVALID_OPERATION:
            o<<"OpenGL Error in "<<sourceFile<<" at line "<<line<<": Invalid operation!"<<endl;
            throw GLErrorException(o.str());
            break;

        case GL_STACK_OVERFLOW:
            o<<"OpenGL Error in "<<sourceFile<<" at line "<<line<<": Stack overflow!"<<endl;
            throw GLErrorException(o.str());
            break;

        case GL_STACK_UNDERFLOW:
            o<<"OpenGL Error in "<<sourceFile<<" at line "<<line<<": Stack underflow!"<<endl;
            throw GLErrorException(o.str());
            break;

        case GL_OUT_OF_MEMORY:
            o<<"OpenGL Error in "<<sourceFile<<" at line "<<line<<": Out Of memory!"<<endl;
            throw GLErrorException(o.str());
            break;

        case GL_TABLE_TOO_LARGE:
            o<<"OpenGL Error in "<<sourceFile<<" at line "<<line<<": Table too large!"<<endl;
            throw GLErrorException(o.str());
            break;

        default:
            o<<"OpenGL Error in "<<sourceFile<<" at line "<<line<<": Unknown error!"<<endl;
            throw GLErrorException(o.str());
            break;
    }
}

【问题讨论】:

  • 您使用哪个版本的 OpenGL?帧缓冲区对象函数没有任何供应商后缀,所以我倾向于相信 3.0 - 3.3,但我很怀疑。请记住,OpenGL 3 核心功能 GL_ARB_framebuffer_object 和 GL_EXT_framebuffer_object 之间存在重大差异。他们不平等!此外,仅调用 glCheckFramebufferStatus() 是不够的。确保使用 glGetError() 处理正常的 OpenGL 错误
  • 你的假设是正确的。我正在使用 OpenGL 3.1。我应该早点提到这一点。感谢 glGetError() 提示。我将其包含在我的代码中。我将该方法添加到上面的代码清单中。我想我检查了 OpenGL 3.1 可能引发的所有错误,不是吗?我检查了整个代码是否有错误,但似乎什么都没有。
  • 我越来越怀疑不是应用程序包含错误,而是可能分别缺少某些库或扩展名或设置错误。我不确定这是否有帮助,但这是我的系统:Windows 7、OpenGL 3.1、NVidia Quadro FX 3700。我正在使用 GLEW 1.5.7、SDL 1.2、GL_EXT_framebuffer_object、GL_EXT_texture3D、GL_ARB_draw_buffers... 任何想法表示赞赏。

标签: opengl rendering render texture-mapping framebuffer


【解决方案1】:

我解决了这个问题。

所以这就是我所做的。也许有一些更好/更智能的方法可以做到这一点,但现在它工作得很好。随意提出改编建议...

我没有在下面列出错误处理。您可以在这个问题中进一步找到。


// create frame buffer object
glGenFramebuffers(1, frameBufferObject);

// create empty texture
int width = 512;
int height = 512;
int numberOfChannels = 3;
GLuint internalFormat = GL_RGB8;
GLuint format = GL_RGB;

unsigned char* texels = new unsigned char[width * height * numberOfChannels];

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

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

glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, texels);

delete[] texels;
texels = NULL;

// draw the colored quad into the initially empty texture
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);

// store attibutes
glPushAttrib(GL_VIEWPORT_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// reset viewport
glViewport(0, 0, width, height);

// setup modelview matrix
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();

// setup projection matrix
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();

// setup orthogonal projection
glOrtho(-width / 2, width / 2, -height / 2, height / 2, 0, 1000);

// bind framebuffer object
glBindFramebuffer(GL_FRAMEBUFFER, frameBufferObject);

// attach empty texture to framebuffer object
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);

// check framebuffer status (see above)

// bind framebuffer object (IMPORTANT! bind before adding color attachments!)
glBindFramebuffer(GL_FRAMEBUFFER, frameBufferObject);

// define render targets (empty texture is at GL_COLOR_ATTACHMENT0)
glDrawBuffers(1, GL_COLOR_ATTACHMENT0); // you can of course have more than only 1 attachment

// activate & bind empty texture
// I figured activating and binding must take place AFTER attaching texture as color attachment
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);

// clear color attachments
glClear(GL_COLOR_BUFFER_BIT);

// make background yellow
glClearColor(1.0f, 1.0f, 0.0f, 1.0f);

// draw quad into texture attached to frame buffer object
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glBegin(GL_QUADS);
    glColor4f(1.0f, 1.0f, 1.0f, 1.0f); glVertex2f(0.0f, 100.0f); // top left
    glColor4f(1.0f, 0.0f, 0.0f, 1.0f); glVertex2f(0.0f, 0.0f); // bottom left
    glColor4f(0.0f, 1.0f, 0.0f, 1.0f); glVertex2f(100.0f, 0.0f); // bottom right
    glColor4f(0.0f, 0.0f, 1.0f, 1.0f); glVertex2f(100.0f, 100.0f); // top right
glEnd();

// reset projection matrix
glMatrixMode(GL_PROJECTION);
glPopMatrix();

// reset modelview matrix
glMatrixMode(GL_MODELVIEW);
glPopMatrix();

// restore attributes
glPopAttrib();

glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);

// I guess, now it's OK to create MipMaps

// draw the scene
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();

glColor4f(1.0, 1.0, 1.0, 1.0);

// begin texture mapping
glEnable(GL_TEXTURE_2D);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    // normal faces "camera"
    glNormal3d(0.0f, 0.0f, 1.0f);

    glBegin(GL_QUADS);
        glNormal3d(0.0f, 0.0f, 1.0f);
        glTexCoord2f(0.0f, 0.0f); glVertex3f(0.0f, 50.0f, -100.0f);    // top left
        glTexCoord2f(0.0f, 1.0f); glVertex3f(0.0f, 0.0f, -100.0f);     // bottom left
        glTexCoord2f(1.0f, 1.0f); glVertex3f(50.0f, 0.0f, -100.0f);    // bottom right
        glTexCoord2f(1.0f, 0.0f); glVertex3f(50.0f, 50.0f, -100.0f);   // top right
    glEnd();

glDisable(GL_TEXTURE_2D);

glPopMatrix();

// finish rendering
glFlush();
glFinish();

// swap buffers (I forgot to mention that I use SDL)
SDL_GL_SwapBuffers();

// do the clean up!

如您所见,我摆脱了深度缓冲区附件。我认为我的任务并不需要它。一旦我真的可以画东西,我就添加了正交投影。没有它,绘制到纹理会导致非常尴尬的图像......

希望这对那里的人有用 沃尔特

【讨论】:

  • 听起来像是深度缓冲区附件,但如果是的话,我希望它会出错。
  • 您应该将 malloc 用于大型数组,否则您实际上会堆栈溢出;)
【解决方案2】:

很奇怪,glDrawBuffersglFramebufferTexture2D 看起来都不像规格!第一个采用 GLenum 的 array,而不是 GLenum(检查警告:它可以被静默投射!)。而第二个在规格上只需要 4 个参数!

编辑:

void glDrawBuffers(GLsizei  n,  const GLenum * bufs);

【讨论】:

  • 好收获。 glFramebufferTexture2D 是因为 EXT 版本有 5 个参数,Core 版本有 4 个。不需要 GL_TEXTURE_2D。
  • 好吧,glFramebufferTexture2D 确实只需要 4 个参数。但是如果我省略“GL_TEXTURE_2D”,我就无法编译应用程序。错误 C2198:'PFNGLFRAMEBUFFERTEXTURE2DPROC':调用的参数太少我猜还有一些错误/缺失。我要检查我是否真的拥有最新版本的 glew.h
  • 我对另一种说法有点困惑。 “glDrawBuffers(1, GL_GL_COLOR_ATTACHMENTn)”不是很好吗?引用:“片段着色器输出值被写入当前帧缓冲区的第 n 个颜色附件。n 的范围可以从 0 到 GL_MAX_COLOR_ATTACHMENTS 的值。”
  • 我为glDrawBuffers函数添加了签名。我认为您需要使用:GLenum att = GL_GL_COLOR_ATTACHMENT;,然后是glDrawBuffers(1,&amp;att);,因为glDrawBuffers 需要一个 GLenum 数组作为参数。
  • 我认为你可能是对的,glDrawBuffers() 有问题。我这里有两种不同的规格。你给的一个,另一个,用德语写的。后者说的基本相同,但没有提到我可以使用 GL_COLOR_ATTACHMENTn。相反,它说可以使用 GL_AUXi ,奇怪的是,在英文规范中没有提到。但是,GL_AUXi 会默默地抛出错误!它给出了“GL_INVALID_OPERATION”。 GL_FRONT_LEFT、GL_FRONT_RIGHT、GL_BACK_LEFT 和 GL_BACK_RIGHT 相同。只有 GL_COLOR_ATTACHMENTn 实际上似乎不会导致错误。
【解决方案3】:

我预计会发生什么:

  • 您正在从未填充的 mip-map 级别进行渲染。您在渲染彩色四边形之前就调用了 glGenerateMipmap。现在就试试 GL_NEAREST。
  • 创建帧缓冲区对象时出现错误。如果发生这种情况,您的渲染只会掉进黑洞。

这是我用来检查帧缓冲区状态的 sn-p 代码。它使用 FBO 的 EXT 版本并且在 Objective-C 中(因此是 @"" 字符串),但它应该是显而易见的。它是在我调用 glBindFramebuffer、glFramebufferTexture2D 和 glDrawBuffers 之后出现的。

    GLenum framebufferStatus = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);

    switch (framebufferStatus) {
        case GL_FRAMEBUFFER_COMPLETE_EXT: break;
        case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
            NSLog(@"Framebuffer Object %d Error: Attachment Point Unconnected", i);
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
            NSLog(@"Framebuffer Object %d Error: Missing Attachment", i);
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
            NSLog(@"Framebuffer Object %d Error: Dimensions do not match", i);
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
            NSLog(@"Framebuffer Object %d Error: Formats", i);
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
            NSLog(@"Framebuffer Object %d Error: Draw Buffer", i);
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
            NSLog(@"Framebuffer Object %d Error: Read Buffer", i);
            break;
        case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
            NSLog(@"Framebuffer Object %d Error: Unsupported Framebuffer Configuration", i);
            break;
        default:
            NSLog(@"Framebuffer Object %d Error: Unkown Framebuffer Object Failure", i);
            break;
    }

问题可能出在深度缓冲区上,因为驱动程序往往对此非常挑剔。对于此测试,您不需要深度,因此如果它导致问题,您可以删除渲染缓冲区内容。

另一个注意事项,纹理不需要绑定和激活即可渲染到它。您可以推迟该代码,直到您想使用它来渲染最终图像。

【讨论】:

  • 对不起,我错过了上面代码中的错误处理。我确实检查了帧缓冲区状态。但是,我认为我不会检查所有可能的错误。我忘了检查 GL_FRAMEBUFFER_INCOMPLETE_DIMENSION_EXT 和 GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT ,它们当然非常重要。我添加了这两个案例。不幸的是,这既没有影响,也没有返回错误消息。我已将错误检查添加到上述代码中。
  • 我尝试了 GL_NEAREST 而不是 GL_LINEAR 并省略了 mipmap 生成。也不行。
  • 我现在也不会在实际纹理映射之前激活和绑定纹理。还是一样……
猜你喜欢
  • 2014-07-08
  • 1970-01-01
  • 1970-01-01
  • 2012-04-24
  • 1970-01-01
  • 2011-09-06
  • 1970-01-01
  • 1970-01-01
  • 2023-03-06
相关资源
最近更新 更多