OpenGL + SDL + 无法绘制到屏幕外 FBO

OpenGL + SDL + Cannot Draw to Offscreen FBO

我有 2 个程序 - 都是 OpenGL 3.x:

两个程序都调用这个函数:

int DrawGLScene( GLvoid )                               
{
  static int nFirstTime = 1;

  if( nFirstTime )
  {
    glewInit();

    nFirstTime = 0;
    glGenTextures( 1, &g_OffscreenTexture );
    glGenFramebuffers( 1, &g_OffscreenFBO );
  }

  GLuint nScreenFBO = 0;

  glBindTexture( GL_TEXTURE_2D, 0 );

  SelectColor( COLOR_YELLOW );  
  DrawFilledRectangle( 50, 50, 200, 200 );

  // Now draw to offscreen FBO..
  glGetIntegerv( GL_FRAMEBUFFER_BINDING, (GLint *) &nScreenFBO );

  glBindFramebuffer( GL_FRAMEBUFFER, g_OffscreenFBO );
  glBindTexture( GL_TEXTURE_2D, g_OffscreenTexture );

  //glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, g_ScreenWidth, g_ScreenHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0 );  // WORKS
  glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0 ); // DOESN'T WORK

  glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, g_OffscreenTexture, 0 );

  SelectColor( COLOR_GREEN );  
  DrawFilledRectangle( 0, 0, 200, 200 );

  SelectColor( COLOR_RED );  
  DrawFilledRectangle( 0, 50, 200, 200 );

  // Go test the colours
  TestRGB();

  // Put screen FBO back..
  glBindTexture( GL_TEXTURE_2D, 0 );
  glBindFramebuffer( GL_FRAMEBUFFER, nScreenFBO );

  return TRUE;
}

该函数在可见屏幕上绘制一个黄色矩形,应该在屏幕外 FBO 中绘制几个绿色和红色矩形。

然后我使用调用 glReadPixels() 的函数 (TestRGB()) return 屏幕外 FBO 中红色和绿色矩形内各点的 RGB 值。

glReadPixels() 在非 SDL 程序中工作正常。对于 SDL 程序,它总是 returns RGB 值 0(黑色)。

当我更改屏幕外 FBO 的 glTexImage2D() 尺寸以匹配实际屏幕时,SDL 版本有效!

注意:我希望屏幕外的纹理比屏幕小。

我做错了什么?我错过了纹理初始化步骤吗?

这是我用来获取 RGB 值的函数:

void GetRGBAt( GLuint nFBO, int x, int y, GLfloat *pfR, GLfloat *pfG, GLfloat *pfB )
{
  GLuint nScreenFBO = 0;

  // Remember the screen FBO..
  glGetIntegerv( GL_FRAMEBUFFER_BINDING, (GLint *) &nScreenFBO );

  // Now bind to given FBO where we'll pull the colours from..
  glBindFramebuffer( GL_FRAMEBUFFER, nFBO );

  // windowHeight - y - 1
  BYTE abRGBA[ 4 ] = { 0 };
  glReadPixels( x, g_ScreenHeight - y - 1, 1, 1, GL_RGBA,   GL_UNSIGNED_BYTE, abRGBA );

  (*pfR) = (float) abRGBA[ 0 ] / 255.0f;
  (*pfG) = (float) abRGBA[ 1 ] / 255.0f;
  (*pfB) = (float) abRGBA[ 2 ] / 255.0f;

  // Put screen FBO back..
  glBindFramebuffer( GL_FRAMEBUFFER, nScreenFBO );
}

这是我的 SDL window 初始化 - 不确定双缓冲是否是造成这种情况的原因。

  if( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER ) < 0 )
  {
    LogDebugf( "FATAL - SDL_Init" );
    exit( -1 );
  }

  atexit( SDL_Quit );

  // Use OpenGL 3.1 core 
  SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3 );
  SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 1 ); 
  SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE );

  SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
  SDL_GL_SetAttribute( SDL_GL_ACCELERATED_VISUAL, 1 );
  SDL_GL_SetAttribute( SDL_GL_RED_SIZE,     8 );
  SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE,   8 );
  SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE,    8 );
  SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE,   8 );
  SDL_GL_SetAttribute( SDL_GL_BUFFER_SIZE, 32 );
  SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE,  24 );

  SDL_DisplayMode currentDisplay;

  SDL_GetCurrentDisplayMode( 0, &currentDisplay );

  g_ScreenWidth  = 960;
  g_ScreenHeight = 640;

  int nWindowPosY = SDL_WINDOWPOS_UNDEFINED;

  SDL_DisplayMode displayMode;
  SDL_GetDesktopDisplayMode( 0, &displayMode );

  g_SDLWindow = SDL_CreateWindow( "OffscreenFBO",
                                 SDL_WINDOWPOS_UNDEFINED,
                                 nWindowPosY,
                                 g_ScreenWidth,
                                 g_ScreenHeight,
                                /* SDL_WINDOW_FULLSCFREEN | */ SDL_WINDOW_OPENGL );
  if( g_SDLWindow == NULL )
  {
    LogDebugf( "SDL_CreateWindow.. FAILED" );
    exit( -1 );
  }

  g_SDLWindowID = SDL_GetWindowID( g_SDLWindow );

  SDL_GL_CreateContext( g_SDLWindow );

  GLenum glewRC = glewInit();

  if( glewRC != GLEW_OK )
    exit( 1 );

  glViewport( 0, 0, g_ScreenWidth, g_ScreenHeight );                    // Reset The Current Viewport
  glClearColor( 0.9f, 0.9f, 0.9f, 1.0f );

  g_Ortho = glm::ortho( 0.0f, (GLfloat) g_ScreenWidth, (GLfloat) g_ScreenHeight, 0.0f, 0.0f, 1000.0f );

  g_SolidProgram            = BuildProgram( vsSolid, fsSolid );

  glClearColor( 0.215f, 0.2f, 0.176f, 1.0f );
  glClear( GL_COLOR_BUFFER_BIT );

  SDL_Event     event;

  while( g_Running )
  {
    DrawGLScene();
    ...
  }

更新: 我发现纹理的大小似乎与我的问题有关。

  //glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, g_ScreenWidth, g_ScreenHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0 );  // WORKS
  glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 1024, 1024, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0 ); // WORKS
  //glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0 ); // DOESN'T WORK

Khronos 声明如下:

width
    Specifies the width of the texture image. All implementations support texture images that are at least 1024 texels wide.
height
    Specifies the height of the texture image, or the number of layers in a texture array, in the case of the GL_TEXTURE_1D_ARRAY and GL_PROXY_TEXTURE_1D_ARRAY targets. All implementations support 2D texture images that are at least 1024 texels high, and texture arrays that are at least 256 layers deep. 

在我看来,好像 SDL2 实现不支持小于 1024 像素的纹理 wide/high - 至少出于绘制 FBO 的目的。

这不起作用的主要原因是我忘记在屏幕外 FBO 上应用正确的正射投影矩阵。我使用与屏幕 (1024x640) 相同的投影,其中纹理矩阵应该使用 512x512。此外,GetRGBAt() 代码使用 g_ScreenHeight 而不是纹理的高度。只要我设置了投影,它现在可以很好地处理许多纹理高度。

换句话说,对屏幕外 FBO 的绘制不正确。