PDA

View Full Version : Having trouble with 2nd layer / nested FrameBufferObjects



Necreia
02-14-2013, 03:05 PM
I'm trying to render to a FrameBufferObject, and then render that to another FrameBufferObject. Since I couldn't get it working in my game engine, I ran out to the net and cobbled together a really basic example in order to remove impacting variables. Even after this, I just can't get it. See here:



uint16 mDisplayHeight = 1000;
uint16 mDisplayWidth = 1600;
int32 mVideoFlags;

// Init SDL
SDL_Init(SDL_INIT_VIDEO);
mVideoFlags = SDL_OPENGL; // Use OpenGL in SDL
mVideoFlags |= SDL_HWPALETTE; // Hardware enabled palette
const SDL_VideoInfo* sdlVideoInfo = SDL_GetVideoInfo();
if (sdlVideoInfo->hw_available)
mVideoFlags |= SDL_HWSURFACE; // Hardware enabled surfaces
else
mVideoFlags |= SDL_SWSURFACE; // Software enabled surfaces
if (sdlVideoInfo->blit_hw)
mVideoFlags |= SDL_HWACCEL; // Hardware enabled blitting
SDL_SetVideoMode(mDisplayWidth, mDisplayHeight, 32, mVideoFlags);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);

// Initialize OpenGL
glewInit();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_DEPTH_TEST);

/** First FBO - The Render One **/
uint32 mFinalFBOTexID = 0;
uint32 mFinalFBOID = 0;
glGenFramebuffers(1, &mFinalFBOID);
glBindFramebuffer(GL_FRAMEBUFFER, mFinalFBOID);
glGenTextures(1, &mFinalFBOTexID);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mFinalFBOTexID);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, mDisplayWidth,
mDisplayHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB,
mFinalFBOTexID, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
/****/

/** Second FBO - Intermediate **/
uint32 mSecondRenderTextureID = 0;
uint32 mSecondRenderTextureFBO = 0;
glGenFramebuffers(1, &mSecondRenderTextureFBO);
glBindFramebuffer(GL_FRAMEBUFFER, mSecondRenderTextureFBO);
glGenTextures(1, &mSecondRenderTextureID);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mSecondRenderTextureID);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, 300,
300, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB,
mSecondRenderTextureID, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
/****/

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, mDisplayWidth, mDisplayHeight, 0, -1, 1);
glMatrixMode(GL_MODELVIEW);

glEnable(GL_TEXTURE_RECTANGLE_ARB);

// Execution Loop
while(true)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glBindFramebuffer(GL_FRAMEBUFFER, mFinalFBOID); // X1
//glBindFramebuffer(GL_FRAMEBUFFER, mSecondRenderTextureFBO); // X2

// Draw a quad
glDisable(GL_TEXTURE_RECTANGLE_ARB);
glColor3f(1.0f, 1.0f, 0.0f);
glBegin(GL_QUADS);
glVertex2i(10, 10);
glVertex2i(50, 10);
glVertex2i(50, 50);
glVertex2i(10, 50);
glEnd();
glEnable(GL_TEXTURE_RECTANGLE_ARB);

/* X2
glBindFramebuffer(GL_FRAMEBUFFER, mFinalFBOID);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mSecondRenderTextureID);

glBegin(GL_QUADS);
glTexCoord2i(0, 100);
glVertex2i(0, 0);
glTexCoord2i(100, 100);
glVertex2i(100, 0);
glTexCoord2i(100, 0);
glVertex2i(100, 100);
glTexCoord2i(0, 0);
glVertex2i(0, 100);
glEnd();
*/

// Draw the 2D Layer on top
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glLoadIdentity();
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mFinalFBOTexID);

glBegin(GL_QUADS);
glTexCoord2i(0, mDisplayHeight);
glVertex2i(0, 0);
glTexCoord2i(mDisplayWidth, mDisplayHeight);
glVertex2i(mDisplayWidth, 0);
glTexCoord2i(mDisplayWidth, 0);
glVertex2i(mDisplayWidth, mDisplayHeight);
glTexCoord2i(0, 0);
glVertex2i(0, mDisplayHeight);
glEnd();

// Present the information
SDL_GL_SwapBuffers();
}


This code renders a yellow quad without any problems. But, if I uncomment the two X2 blocks and comment out the X1 block, I get a black screen. I can't seem to figure out how to paint to and render that second FBO. What am I missing?

debonair
02-14-2013, 11:38 PM
For x1 you have created mFinalFBOTexID and while rendering on default FBO you are binding this texture which is as per expected. But in x2, looks like you have created another texture "mSecondRenderTextureID" and while rendering on default FBO you are binding mFinalFBOTexID. i assume you dont have another texture rendered on x2 which is "mSecondRenderTextureID", if this is so you must bind mFinalFBOTexID to x2 and not mSecondRenderTextureID.

One more, make sure that you have appropriate height width in glTexImage2D call for second FBO. Try changing it to as per glTeximage2D call for 1st FBO.

Necreia
02-15-2013, 08:18 AM
Good catch. After some tinkering and your direction, it now works. So that googlers can pull up a working example-- this works:


uint16 mDisplayHeight = 1000;
uint16 mDisplayWidth = 1600;
int32 mVideoFlags;

// Init SDL
SDL_Init(SDL_INIT_VIDEO);
mVideoFlags = SDL_OPENGL; // Use OpenGL in SDL
mVideoFlags |= SDL_HWPALETTE; // Hardware enabled palette
const SDL_VideoInfo* sdlVideoInfo = SDL_GetVideoInfo();
if (sdlVideoInfo->hw_available)
mVideoFlags |= SDL_HWSURFACE; // Hardware enabled surfaces
else
mVideoFlags |= SDL_SWSURFACE; // Software enabled surfaces
if (sdlVideoInfo->blit_hw)
mVideoFlags |= SDL_HWACCEL; // Hardware enabled blitting
SDL_SetVideoMode(mDisplayWidth, mDisplayHeight, 32, mVideoFlags);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);

// Initialize OpenGL
glewInit();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_DEPTH_TEST);

// Create the frame buffers and textures
unsigned int mFrameBufferTexIDs[2];
unsigned int mFrameBufferObjectIDs[2];
for (int i = 0; i < 2; i++)
{
glGenFramebuffers(1, &mFrameBufferObjectIDs[i]);
glBindFramebuffer(GL_FRAMEBUFFER, mFrameBufferObjectIDs[i]);
glGenTextures(1, &mFrameBufferTexIDs[i]);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mFrameBufferTexIDs[i]);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, mDisplayWidth, mDisplayHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, mFrameBufferTexIDs[i], 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, mDisplayWidth, mDisplayHeight, 0, -1, 1);
glMatrixMode(GL_MODELVIEW);

// Framebuffer and texture arrays are as follows:
// 0 : Final output (what I want to present to the screen)
// 1 : Intermediate output (what I want to render to and then render THAT to the screen)
bool renderToIntermediateFBO = true;

while(true)
{
if (renderToIntermediateFBO == false)
{
glBindFramebuffer(GL_FRAMEBUFFER, mFrameBufferObjectIDs[0]);

// Draw a quad with no texture
glDisable(GL_TEXTURE_RECTANGLE_ARB);
glColor3f(1.0f, 1.0f, 0.0f);
glBegin(GL_QUADS);
glVertex2i(10, 10);
glVertex2i(50, 10);
glVertex2i(50, 50);
glVertex2i(10, 50);
glEnd();
glEnable(GL_TEXTURE_RECTANGLE_ARB);
}
else
{
glBindFramebuffer(GL_FRAMEBUFFER, mFrameBufferObjectIDs[1]);

// Draw a quad with no texture
glDisable(GL_TEXTURE_RECTANGLE_ARB);
glColor3f(1.0f, 1.0f, 0.0f);
glBegin(GL_QUADS);
glVertex2i(10, 10);
glVertex2i(50, 10);
glVertex2i(50, 50);
glVertex2i(10, 50);
glEnd();
glEnable(GL_TEXTURE_RECTANGLE_ARB);

// Draw the intermediate FBO onto the final FBO
glBindFramebuffer(GL_FRAMEBUFFER, mFrameBufferObjectIDs[0]);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mFrameBufferTexIDs[1]);
glBegin(GL_QUADS);
glTexCoord2i(0, mDisplayHeight);
glVertex2i(0, 0);
glTexCoord2i(mDisplayWidth, mDisplayHeight);
glVertex2i(mDisplayWidth, 0);
glTexCoord2i(mDisplayWidth, 0);
glVertex2i(mDisplayWidth, mDisplayHeight);
glTexCoord2i(0, 0);
glVertex2i(0, mDisplayHeight);
glEnd();

// Draw another untextured quad on the final FBO
glDisable(GL_TEXTURE_RECTANGLE_ARB);
glColor3f(0.0f, 1.0f, 1.0f);
glBegin(GL_QUADS);
glVertex2i(20, 20);
glVertex2i(60, 20);
glVertex2i(60, 60);
glVertex2i(20, 60);
glEnd();
glEnable(GL_TEXTURE_RECTANGLE_ARB);
}

// Present the final information to the screen
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glLoadIdentity();
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mFrameBufferTexIDs[0]);
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_QUADS);
glTexCoord2i(0, mDisplayHeight);
glVertex2i(0, 0);
glTexCoord2i(mDisplayWidth, mDisplayHeight);
glVertex2i(mDisplayWidth, 0);
glTexCoord2i(mDisplayWidth, 0);
glVertex2i(mDisplayWidth, mDisplayHeight);
glTexCoord2i(0, 0);
glVertex2i(0, mDisplayHeight);
glEnd();
SDL_GL_SwapBuffers();
}

debonair
02-15-2013, 09:00 AM
Glad to hear that :)

Necreia
02-15-2013, 09:07 AM
Hmm, well-- this does still leave me with a question. How do I perform this sort of logic with different sized framebufferobjects? If I flip the intermediate Texture to 512x512, I can't get it to work anymore. Is there something else I need to be doing to make FBO's different sizes? Is that even supported?

debonair
02-15-2013, 09:46 AM
AFIK, you can't have texture size greater than that of your attachments, plus all your attachments to FBO should be of equal sizes. But you can create texture of lower size than your FBOs so that at-least you wont get any fault during run but it will left you with loss of FBO data.
So i would suggest if you have textures of varying sizes, make sure that your FBOs have size greater than your largest possible value of texture size.

Necreia
02-15-2013, 10:07 AM
Conceptually, that makes perfect sense. But it looks like I'm running into the same problem I had originally when I apply it. See here for an example. I manually set the second (intermediate) FBO texture to be 512x512, and render within those paremeters. Now that quad isn't rendering anymore. If I change mDisplayHeight and mDisplayWidth to both be 512, then it renders (since now both the final and the intermediate are 512x512). But as long as those are a higher value, all I get is black.


unsigned short mDisplayHeight = 1000;
unsigned short mDisplayWidth = 1600;
int mVideoFlags;

// Init SDL
SDL_Init(SDL_INIT_VIDEO);
mVideoFlags = SDL_OPENGL; // Use OpenGL in SDL
mVideoFlags |= SDL_HWPALETTE; // Hardware enabled palette
const SDL_VideoInfo* sdlVideoInfo = SDL_GetVideoInfo();
if (sdlVideoInfo->hw_available)
mVideoFlags |= SDL_HWSURFACE; // Hardware enabled surfaces
else
mVideoFlags |= SDL_SWSURFACE; // Software enabled surfaces
if (sdlVideoInfo->blit_hw)
mVideoFlags |= SDL_HWACCEL; // Hardware enabled blitting
SDL_SetVideoMode(mDisplayWidth, mDisplayHeight, 32, mVideoFlags);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);

// Initialize OpenGL
glewInit();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_DEPTH_TEST);

// Create the frame buffers and textures
unsigned int mFrameBufferTexIDs[2];
unsigned int mFrameBufferObjectIDs[2];

glGenFramebuffers(1, &mFrameBufferObjectIDs[0]);
glBindFramebuffer(GL_FRAMEBUFFER, mFrameBufferObjectIDs[0]);
glGenTextures(1, &mFrameBufferTexIDs[0]);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mFrameBufferTexIDs[0]);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, mDisplayWidth, mDisplayHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, mFrameBufferTexIDs[0], 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);

unsigned short intermediateTexWidth = 512;
unsigned short intermediateTexHeight = 512;
glGenFramebuffers(1, &mFrameBufferObjectIDs[1]);
glBindFramebuffer(GL_FRAMEBUFFER, mFrameBufferObjectIDs[1]);
glGenTextures(1, &mFrameBufferTexIDs[1]);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mFrameBufferTexIDs[1]);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, intermediateTexWidth, intermediateTexHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, mFrameBufferTexIDs[1], 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, mDisplayWidth, mDisplayHeight, 0, -1, 1);
glMatrixMode(GL_MODELVIEW);

// Framebuffer and texture arrays are as follows:
// 0 : Final output (what I want to present to the screen)
// 1 : Intermediate output (what I want to render to and then render THAT to the screen)
while(true)
{
glBindFramebuffer(GL_FRAMEBUFFER, mFrameBufferObjectIDs[1]);

// Draw a quad with no texture
glDisable(GL_TEXTURE_RECTANGLE_ARB);
glColor3f(1.0f, 1.0f, 0.0f);
glBegin(GL_QUADS);
glVertex2i(10, 10);
glVertex2i(50, 10);
glVertex2i(50, 50);
glVertex2i(10, 50);
glEnd();
glEnable(GL_TEXTURE_RECTANGLE_ARB);

// Draw the intermediate FBO onto the final FBO
glBindFramebuffer(GL_FRAMEBUFFER, mFrameBufferObjectIDs[0]);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mFrameBufferTexIDs[1]);
glBegin(GL_QUADS);
glTexCoord2i(0, intermediateTexHeight);
glVertex2i(0, 0);
glTexCoord2i(intermediateTexWidth, intermediateTexHeight);
glVertex2i(intermediateTexWidth, 0);
glTexCoord2i(intermediateTexWidth, 0);
glVertex2i(intermediateTexWidth, intermediateTexHeight);
glTexCoord2i(0, 0);
glVertex2i(0, intermediateTexHeight);
glEnd();

// Present the final information to the screen
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glLoadIdentity();
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mFrameBufferTexIDs[0]);
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_QUADS);
glTexCoord2i(0, mDisplayHeight);
glVertex2i(0, 0);
glTexCoord2i(mDisplayWidth, mDisplayHeight);
glVertex2i(mDisplayWidth, 0);
glTexCoord2i(mDisplayWidth, 0);
glVertex2i(mDisplayWidth, mDisplayHeight);
glTexCoord2i(0, 0);
glVertex2i(0, mDisplayHeight);
glEnd();
SDL_GL_SwapBuffers();
}

Perhaps I'm approaching the problem wrong. Should I be using a single FBO, but having two separate texture attachments? And if so, how do I change which texture attachment is active in a FBO at a time?

Necreia
02-15-2013, 10:23 AM
Okay... I figured it out. Here's a working code example:


unsigned short mDisplayHeight = 1000;
unsigned short mDisplayWidth = 1600;
int mVideoFlags;

// Init SDL
SDL_Init(SDL_INIT_VIDEO);
mVideoFlags = SDL_OPENGL; // Use OpenGL in SDL
mVideoFlags |= SDL_HWPALETTE; // Hardware enabled palette
const SDL_VideoInfo* sdlVideoInfo = SDL_GetVideoInfo();
if (sdlVideoInfo->hw_available)
mVideoFlags |= SDL_HWSURFACE; // Hardware enabled surfaces
else
mVideoFlags |= SDL_SWSURFACE; // Software enabled surfaces
if (sdlVideoInfo->blit_hw)
mVideoFlags |= SDL_HWACCEL; // Hardware enabled blitting
SDL_SetVideoMode(mDisplayWidth, mDisplayHeight, 32, mVideoFlags);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);

// Initialize OpenGL
glewInit();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_DEPTH_TEST);

// Create the frame buffers and textures
unsigned int mFrameBufferTexIDs[2];
unsigned int mFrameBufferObjectIDs[2];

glGenFramebuffers(1, &mFrameBufferObjectIDs[0]);
glBindFramebuffer(GL_FRAMEBUFFER, mFrameBufferObjectIDs[0]);
glGenTextures(1, &mFrameBufferTexIDs[0]);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mFrameBufferTexIDs[0]);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, mDisplayWidth, mDisplayHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, mFrameBufferTexIDs[0], 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);

unsigned short intermediateTexWidth = 512;
unsigned short intermediateTexHeight = 512;
glGenFramebuffers(1, &mFrameBufferObjectIDs[1]);
glBindFramebuffer(GL_FRAMEBUFFER, mFrameBufferObjectIDs[1]);
glGenTextures(1, &mFrameBufferTexIDs[1]);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mFrameBufferTexIDs[1]);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, intermediateTexWidth, intermediateTexHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, mFrameBufferTexIDs[1], 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, mDisplayWidth, mDisplayHeight, 0, -1, 1);
glMatrixMode(GL_MODELVIEW);

// Framebuffer and texture arrays are as follows:
// 0 : Final output (what I want to present to the screen)
// 1 : Intermediate output (what I want to render to and then render THAT to the screen)
while(true)
{
glBindFramebuffer(GL_FRAMEBUFFER, mFrameBufferObjectIDs[1]);
glLoadIdentity();
glTranslated(0, mDisplayHeight-intermediateTexHeight, 0);

// Draw a quad with no texture
glDisable(GL_TEXTURE_RECTANGLE_ARB);
glColor3f(1.0f, 1.0f, 0.0f);
glBegin(GL_QUADS);
glVertex2i(10, 10);
glVertex2i(50, 10);
glVertex2i(50, 50);
glVertex2i(10, 50);
glEnd();
glEnable(GL_TEXTURE_RECTANGLE_ARB);

// Draw the intermediate FBO onto the final FBO
glBindFramebuffer(GL_FRAMEBUFFER, mFrameBufferObjectIDs[0]);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mFrameBufferTexIDs[1]);
glBegin(GL_QUADS);
glTexCoord2i(0, 0);
glVertex2i(0, 0);
glTexCoord2i(intermediateTexWidth, 0);
glVertex2i(intermediateTexWidth, 0);
glTexCoord2i(intermediateTexWidth, intermediateTexHeight);
glVertex2i(intermediateTexWidth, intermediateTexHeight);
glTexCoord2i(0, intermediateTexHeight);
glVertex2i(0, intermediateTexHeight);
glEnd();

// Present the final information to the screen
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glLoadIdentity();
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mFrameBufferTexIDs[0]);
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_QUADS);
glTexCoord2i(0, 0);
glVertex2i(0, 0);
glTexCoord2i(mDisplayWidth, 0);
glVertex2i(mDisplayWidth, 0);
glTexCoord2i(mDisplayWidth, mDisplayHeight);
glVertex2i(mDisplayWidth, mDisplayHeight);
glTexCoord2i(0, mDisplayHeight);
glVertex2i(0, mDisplayHeight);
glEnd();

SDL_GL_SwapBuffers();
}

The problem was because of two factors. 1) I'm using ortho with a negative 1 on the top parameter to put the engine in a 2D top-left 0 coordinate space, and 2) FrameBufferObjects render with bottom-left being 0. So to fix it, I had to do the following:
- Before drawing to the smaller intermediate FBO, glTranslated() y parameter was set to FinalHeight - IntermediateHeight
- Invert the y rendering on the intermediate => final quad draw

Thanks for all of your help. I'm good now.