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

Posted

技术标签:

【中文标题】OpenGL - 在 NVIDIA 卡上渲染到纹理时出现 FBO 黑屏【英文标题】:OpenGL - FBO black screen when rendering to texture on NVIDIA card 【发布时间】:2015-09-27 01:02:29 【问题描述】:

我学习了一些关于如何设置和渲染 FBO 的教程。起初它运行良好,场景渲染良好,但事实证明该程序使用的是集成 GPU。 (我正在使用我的笔记本电脑)

然后,出于好奇,我用“更高性能”的 GPU(Nvidia GeForce GT540M)运行它,然后屏幕全黑了。此时我尝试将 FBO 的颜色纹理保存到一个文件中,并且场景实际上正在那里绘制。

但最终我找到了解决方案。以前我只会清除 FBO(颜色和深度缓冲区),但现在我会清除 FBO 和默认帧缓冲区,然后再次渲染场景。

所以问题是,我必须调用 glClear 两次是不是很糟糕?我真的需要调用 glClear 两次吗?为什么一个 clear 能在集成卡上工作?


如果有帮助,我可以展示一些代码。

帧缓冲初始化

bool FrameBufferObject::initializeFBO( int width, int height ) 
    glActiveTexture( GL_TEXTURE0 );

    if ( !_colorTexture.createEmptyTexture( width, height ) ) 
        return false;
    
    _textureId = _colorTexture.getTextureId();

    if ( !_depthTexture.createDepthTexture( width, height ) ) 
        return false;
    
    _depthTextureId = _depthTexture.getTextureId();

    glGenFramebuffers( 1, &_frameBufferID );
    glBindFramebuffer( GL_FRAMEBUFFER, _frameBufferID );
    glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _textureId, 0 );
    glFramebufferTexture2D( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _depthTextureId, 0 );

    if ( glCheckFramebufferStatus( GL_FRAMEBUFFER ) != GL_FRAMEBUFFER_COMPLETE ) 
        return false;
    

    glBindFramebuffer( GL_FRAMEBUFFER, 0 );

    _width = width;
    _height = height;

    _isInitialized = true;

    return true;

颜色纹理

bool Texture::createEmptyTexture( int width, int height ) 
    if ( isLoaded() ) 
        closeTexture();
    

    GLuint textureId = 0;

    glGenTextures( 1, &textureId );

    if ( getOpenGLError( "Unable to generate TextureID." ) ) 
        return false;
    

    glBindTexture( GL_TEXTURE_2D, textureId );
    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
    glBindTexture( GL_TEXTURE_2D, 0 );

    if ( getOpenGLError( "Error creating empty texture." ) ) 
        glDeleteTextures( 1, &textureId );
        return false;
    

    _isLoaded = true;
    _textureWidth = _imageWidth = width;
    _textureHeight = _imageHeight = height;
    _textureId = textureId;

    return true;

深度纹理是相同的,只是它使用 GL_DEPTH_COMPONENT 格式,

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

后处理着色器

顶点着色器

#version 330 core

in vec2 inVertex;
in vec2 inTexture;

out vec2 texCoords;

void main() 
    texCoords = inTexture;
    gl_Position = vec4( inVertex.x, inVertex.y, 0, 1 );

片段着色器

#version 330 core

in vec2 texCoords;

uniform sampler2D texture0;

layout(location = 0) out vec4 outColor;

void main() 
    vec4 color = texture( texture0, texCoords );
    outColor = color;

渲染代码是这样的,

glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

fbo.bindFBO();
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

// .. render scene ..

fbo.unbindFBO();

// This is a namespace
PostProcShader::shader.useShader();
PostProcShader::render( fbo.getColorTexture() );
PostProcShader::shader.disableShader();

SDL_GL_SwapWindow( window );

后处理着色器只是在屏幕大小的四边形上渲染纹理。该场景使用 3 个着色器:一个用于 3D 对象,第二个用于天空盒,最后一个用于字体。

我在 VS 2015 中使用 C++、SDL2 和(当然)OpenGL/Glew。


编辑:

深度测试初始化​​

glEnable( GL_DEPTH_TEST );
glDepthMask( GL_TRUE );
glDepthFunc( GL_LEQUAL );
glDepthRange( 0.0f, 1.0f );
glClearDepth( 1.0f );

【问题讨论】:

当您将 FBO 绘制到屏幕上时,您是否启用了深度测试?你的深度函数是什么? 是的,那么我应该在将 FBO 绘制到屏幕时禁用深度测试吗? (我编辑了问题) 嗯,是的,如果你不想进行深度测试,你应该禁用深度测试。此外,如果您从未对默认帧缓冲区使用深度测试,则可以在没有深度缓冲区的情况下创建它。另一种选择是使用glBlitFramebuffer() 将FBO 内容复制到默认帧缓冲区。然后你就不需要那么多状态设置,不需要额外的着色器等等。 @aslg 你不妨试试。不知道能不能解决你的问题。 glBlitFramebuffer 看起来很有希望,我会试一试。 @immibis 禁用深度测试也可以。 【参考方案1】:

当我从 Intel HUD 转到 Nvidia 时,我遇到了类似的问题。但是我的解决方案很简单。在渲染到新的帧缓冲区(在 Nvidia 上)之前,我必须确保我调用了 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT),否则我将看不到任何渲染。我怀疑在 Nvidia 上,深度缓冲区未设置为与 Intel 相同的默认值,因此清除它会将其设置为 opengl 默认清除值。

【讨论】:

以上是关于OpenGL - 在 NVIDIA 卡上渲染到纹理时出现 FBO 黑屏的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL:随机调用 OpenGL 函数时崩溃

OpenGL中的多个渲染目标与Cg

在 nvidia opengl 上混合 glGetTexImage 和 imageStore 的问题

Intel和Nvidia之间的OpenGL fbo blitting不一致

OpenGL + QT:渲染到纹理并显示回来

OpenGL:渲染到 FBO 时是不是支持纹理组合器功能?