2

I'm using OpengGL on a Mac OS X application to draw texture on a NSOpenGLView.

The app is a movie player. It decodes movie frames into CVOpenGLTextureRef (which are OpenGL texture) and I draw them directly to the view using GL_QUAD. Everything works correctly.

Below is the relevant part of the code.

// "image" is the CVOpenGLTextureRef containing the movie frame as texture
GLenum textureTarget = CVOpenGLTextureGetTarget(image);
GLuint textureName = CVOpenGLTextureGetName(image);

glEnable(textureTarget);
glBindTexture(textureTarget, textureName); 

glBegin(GL_QUADS);
// Draw the quads
//Note: textureTagret is NOT GL_TEXTURE_2D, therefore texture coordinates
//are NOT scaled to [0, 1] 

glTexCoord2f(0.0f, imageRect.size.height);
glVertex2f (0.0f, 0.0f);

glTexCoord2f(0.0f, 0.0f); 
glVertex2f (0.0f, windowRect.size.height); 

glTexCoord2f(imageRect.size.width, 0.0f);
glVertex2f (windowRect.size.width, windowRect.size.height);

glTexCoord2f(imageRect.size.width, imageRect.size.height);
glVertex2f (windowRect.size.width,0.0f); 

glEnd();
glDisable(textureTarget);
glFlush();

It works just fine and I'm able to resize the window and the texture is mapped correctly to the smaller window.

See here for different window size from full screen to 500x280 pixel: Without FBO

I now want to use FBO for rendering to texture and I started making a very simple implementation which consists in rendering the movie frame to an off-screen FBO (texture) and than bind that texture to draw on screen.

Here is the code:

// "image" is the CVOpenGLTextureRef containing the movie frame as texture
GLenum textureTarget = CVOpenGLTextureGetTarget(image);
GLuint textureName = CVOpenGLTextureGetName(image);

////////////////////////////////////////////////////////////////////
// the creation on the FBO is done only once on program start:

GLuint fboId;
GLuint textureId;
float targetWidth = 2048;
float targetHeight = 2048;

// create a texture object
glGenTextures(1, &textureId);
glBindTexture(textureTarget, textureId);
glTexParameterf(textureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameterf(textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(textureTarget, GL_GENERATE_MIPMAP, GL_TRUE); // automatic mipmap
glTexImage2D(textureTarget, 0, GL_RGBA8, targetWidth, targetHeight, 0,
                 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(textureTarget, 0);

// create a framebuffer object
glGenFramebuffersEXT(1, &fboId);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId);

// attach the texture to FBO color attachment point
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
                           textureTarget, textureId, 0);

// check FBO status
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if(status != GL_FRAMEBUFFER_COMPLETE_EXT)
    return FALSE;

// switch back to window-system-provided framebuffer
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
///////////////////////////////////////////////////////////////////////////////

// Render to texture

glEnable(textureTarget);
glBindTexture(textureTarget, textureName);

glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId);
glClear(GL_COLOR_BUFFER_BIT);   

glBegin(GL_QUADS);
// Draw the quads

glTexCoord2f(0.0f, imageRect.size.height);
glVertex2f (0.0f, 0.0f);        

glTexCoord2f(0.0f, 0.0f); 
glVertex2f (0.0f,imageRect.size.height);

glTexCoord2f(imageRect.size.width, 0.0f);
glVertex2f (imageRect.size.width, imageRect.size.height);

glTexCoord2f(imageRect.size.width, imageRect.size.height);
glVertex2f (imageRect.size.width,0.0f);

glEnd();
glFlush();

// Bind newly rendered texture
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
glClear(GL_COLOR_BUFFER_BIT);
glBindTexture(textureTarget, textureId);
glGenerateMipmapEXT(textureTarget);

// draw on-screen
glBegin(GL_QUADS);
// Draw the quads
//Note: textureTagret is NOT GL_TEXTURE_2D, therefore texture coordinates
//are NOT scaled to [0, 1] 

glTexCoord2f(0.0f, imageRect.size.height);
glVertex2f (0.0f, 0.0f);

glTexCoord2f(0.0f, 0.0f); 
glVertex2f (0.0f, windowRect.size.height); 

glTexCoord2f(imageRect.size.width, 0.0f);
glVertex2f (windowRect.size.width, windowRect.size.height);

glTexCoord2f(imageRect.size.width, imageRect.size.height);
glVertex2f (windowRect.size.width,0.0f); 

glEnd();
glDisable(textureTarget);
glFlush();

The code does not work correctly because the image/texture is not only upside-down but texture mapping is also wrong. It works correctly only when the window is full screen, otherwise it behaves really strange.

See image below: With FBO

As you can see, the texture is scaled correctly inside the window but it gets "cropped" proportionally to the difference between full screen window size and actual window size.

I have tried everything without success.

Since this is one of the first time that I use OpenGL, am I missing something? I'm becoming crazy..

Andrea3000
  • 1,018
  • 1
  • 11
  • 26

1 Answers1

2
// Render to texture

glEnable(textureTarget);
glBindTexture(textureTarget, textureName);

glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId);
glClear(GL_COLOR_BUFFER_BIT);   

Im missing setting the viewport and the projection for the framebuffer object here.

glBegin(GL_QUADS);
// Draw the quads

glTexCoord2f(0.0f, imageRect.size.height);
g    lVertex2f (0.0f, 0.0f);        

glTexCoord2f(0.0f, 0.0f); 
glVertex2f (0.0f,imageRect.size.height);

glTexCoord2f(imageRect.size.width, 0.0f);
glVertex2f (imageRect.size.width, imageRect.size.height);

glTexCoord2f(imageRect.size.width, imageRect.size.height);
glVertex2f (imageRect.size.width,0.0f);

glEnd();
glFlush();
datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • Oh..my bad.. Thank you very much for your answer. I was setting viewport and projection only upon window resize. I didn't realize that I was having to specify two different viewports and projections for the FBO AND for the back-buffer. This solves the texture mapping issue but (obviously) the texture is still upside-down/reflected.. I know that I can change the associations between `glTexCoord2f` and `glVertex2f` to fix it but why do I need it? If I draw to the back-buffer directly, the texture is oriented correctly..why do I need to change things when drawing to FBO? – Andrea3000 Feb 06 '12 at 14:18
  • 1
    @Andrea3000: Well, it's flipped, because you draw it upside down. The image origin is the lower left, not in the upper right which is what you seem to assume. As for the setting of viewport and projection: That's about 80% what I'm telling OpenGL newbies – set them in the drawing function, not in the window resize handler. The window resize handler is a bad place for this, as it leads to trouble when using FBOs, or drawing a HUD or similar. All OpenGL operation (except static data uploads) belong into the drawing functions. – datenwolf Feb 06 '12 at 14:30
  • Thank you for your suggestions about OpenGL, I'll keep them in mind ;-) For what is about flipped image: do FBO and back-buffer have different coordinates systems? I ask it to you because I don't understand how can the image be correct when drawn to back-buffer and flipped when drawn to FBO and then to back-buffer. `glTexCoord2f` and `glVertex2f` are the same for both of the case. – Andrea3000 Feb 06 '12 at 14:49
  • @Andrea3000: What most likely happened is, that the movie frame has been stored assuming the origin in the upper left. So you'll have to flip this. Some image formats like TGA have a fixed origin. Others like PNG or BMP allow for both. You'll have to take this into account. OpenGL always assumes the image data to start in the lower left. So either flip the texture coordinates apropriately or reorder the image data before supplying to glTexImage2D. – datenwolf Feb 06 '12 at 16:45
  • Ok, maybe now I get it, thank you for the explaination ;-) If I have understood what you mean, this is what it happens: my movie frame has an origin in the upper left corner. When I render it to the FBO I render it correctly (not flipped) but now the origin is in the bottom left corner. So from now on I have to change the texture coordinates because the origin will always be in the bottom left corner even if I render it to multiple FBO. Is it right? So the render-to-texture is correct and I have to change the second drawing, the ones to the back-buffer. – Andrea3000 Feb 06 '12 at 17:30
  • @Andrea3000: Exactly. The rendering of the movie frame flips it into upright position, but the further processing steps don't require this, so you don't flip. – datenwolf Feb 06 '12 at 18:18
  • Thank you! You have been very helpful ;-) – Andrea3000 Feb 06 '12 at 18:27