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

时间:2017-06-09 10:33:13

标签: opengl textures sdl-2 fbo glreadpixels

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

  • 一个是Win32程序(修改后的NeHe教程),它通过wglCreateContext()和wlgMakeCurrent()
  • 创建一个Window和GL上下文
  • 一个是SDL2程序

两个程序都调用此函数:

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中绘制几个绿色和红色矩形。

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

glReadPixels()在非SDL程序中正常工作。对于SDL程序,它始终返回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窗口初始化 - 不确定双缓冲是否是导致这种情况的原因。

  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像素宽/高的纹理 - 至少为了绘制到FBO中的目的。

1 个答案:

答案 0 :(得分:1)

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

换句话说,正在对屏幕外FBO进行错误的绘图。