OpenGL + SDL + не удается отрисовать на экране FBO

У меня есть 2 программы - обе OpenGL 3.x:

  • одна из них - программа Win32 (модифицированный учебник NeHe), которая создает контекст Window и GL с помощью wglCreateContext() и wlgMakeCurrent()
  • один из них - программа 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(), чтобы вернуть значения RGB в точках внутри красного и зеленого прямоугольников за пределами экрана FBO.

glReadPixels() отлично работает в не-SDL программе. Для программы SDL она всегда возвращает значения RGB 0 (черный).

Когда я изменяю размеры glTexImage2D() внеэкранного FBO в соответствии с реальным экраном, версия 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

Хронос утверждает следующее:

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 ответ

Решение

Основная причина, по которой это не сработало, заключалась в том, что я забыл применить правильную матрицу ортопроекции на внеэкранном FBO. Я использовал ту же проекцию, что и экран (1024x640), где матрица текстуры должна была использовать 512x512. В дополнение к этому код GetRGBAt() использовал g_ScreenHeight вместо высоты текстуры. Теперь он отлично работает с несколькими высотами текстур, пока я устанавливаю проекцию.

Другими словами, рисование в закадровом окне FBO выполнялось неправильно.

Другие вопросы по тегам