2
votes

I have 2 programs - both OpenGL 3.x:

  • one is a Win32 program (a modified NeHe tutorial) which creates a Window and GL context via wglCreateContext() and wlgMakeCurrent()
  • one is an SDL2 progam

Both programs call this function:

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

The function draws a yellow rectangle on the visible screen and should draw a couple of green and red rectangles in an offscreen FBO.

I then use a function (TestRGB() which calls glReadPixels() to return the RGB values at points within the red and green rectangles in the offscreen FBO.

glReadPixels() works fine in the non-SDL program. For the SDL program, it always returns RGB values of 0 (black).

When I change the glTexImage2D() dimensions of the offscreen FBO to match the actual screen, the SDL version works!

Note: I want the offscreen texture to be smaller than the screen.

What am I doing wrong? Have I missed a texture initialisation step?

Here's the function which I use to get the RGB values:

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

And here is my SDL window initialisation - not sure if double buffering is a cause of this.

  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();
    ...
  }

UPDATE: I have found that the size of the texture seems to be related to my problem.

  //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 state the following:

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. 

It seems to me as though an SDL2 implementation won't support textures smaller than 1024 pixels wide/high - at least for the purpose of drawing into an FBO anyway.

1

1 Answers

1
votes

The main reason this wasn't working was because I forgot to apply the correct ortho projection matrix on the offscreen FBO. I was using the same projection as the screen (1024x640) where the texture matrix should have used 512x512. Further to that the GetRGBAt() code was using g_ScreenHeight instead of the height of the texture. It now works fine with a number of texture heights as long as I set the projection.

In other words, drawing was being done to the offscreen FBO incorrectly.