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, ¤tDisplay );
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 выполнялось неправильно.