Suppose if you need to duplicate an OpenGL texture. How you will manage it?
Poor Programmer’s Method
In this way you will be just loading a texture, and duplicate mainly with the help of glGetTexImage and glTexImage APIs. This is method is extremely low performing and will not work with the textures rendered using frame buffer objects. But still the code works fine with the texture you simply load from the file. Here’s the sample snippet for the same.
[sourcecode language='cpp']
GLuint CopyTextureCPU( GLuint nTexID_in, GLsizei width, GLsizei height )
{
// Generate the destination texture
GLuint nTexID_out = 0;
glGenTextures( 1, &nTexID_out );
glBindTexture( GL_TEXTURE_2D, nTexID_out );
glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR );
// bind the source texture and get the texels
unsigned char* buff = new unsigned char[width*height*3 ];
glBindTexture( GL_TEXTURE_2D, nTexID_in);
glGetTexImage( GL_TEXTURE_2D, 0,GL_RGB,GL_UNSIGNED_BYTE, buff );
// bind the output texture and copy the image
glBindTexture( GL_TEXTURE_2D, nTexID_out );
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, width,height, 0, GL_RGB, GL_UNSIGNED_BYTE, buff );
glBindTexture( GL_TEXTURE_2D, 0 );
return nTexID_out;
}
[/sourcecode]
Using Frame buffer Object
This is the best and fast method (in my experience). You are just instructing the GPU to transfer the source texture to the destination. There are two different methods to do this. In first method, you’re creating a frame buffer and attaching the source texture as render texture. Then you will be binding the newly created frame buffer and bind the destination texture for download. With the help of glCopyTexImage, you can download the texture from the attached frame buffer. The issue with this method is, once if you specify the rendering texture created with some size (say 512, 512), you can’t attach a new texture which is having different size that the first one. It’s one of the limitations with Frame buffer object. Before using this code, you’ve to ensure that your hardware supports "GL_EXT_framebuffer_object" extension.
Method 1:
[sourcecode language='cpp']
GLuint CopyTexture1( GLuint nTexID_in, GLsizei width, GLsizei height )
{
GLuint nTexID_out = 0;
GLuint nFbo = 0;
// Create the duplicate texture
glGenTextures( 1, &nTexID_out);
glBindTexture( GL_TEXTURE_2D, nTexID_out );
glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR );
// Allocate memory for texture
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height,
0, GL_RGBA, GL_UNSIGNED_BYTE, 0 );
// Create and bind frame buffer
glGenFramebuffersEXT( 1, &nFbo);
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, nFbo);
glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D, nTexID_in, 0 );
// Copy texture from frame buffer
glCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, width, height );
// Do the cleanup
glBindTexture( GL_TEXTURE_2D, 0 );
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0);
glDeleteFramebuffersEXT( GL_FRAMEBUFFER_EXT, &nFbo );
return nTexID_out;
}
[/sourcecode]
Alternatively you can bind the destination texture to the frame buffer render texture and bind the source texture for texture mapping. This method is also fast as previous one. There are not big differences except the usage of source and destination textures. But in this method, you should have use texture mapping for copying but in the previous method, you can get it using single OpenGL call (glCopyTexImage).
[sourcecode language='cpp']
GLuint CopyTexture2( GLuint nTexID_in, GLsizei width, GLsizei height )
{
GLuint nTexID_out = 0;
GLuint nFbo = 0;
// Create the duplicate texture
glGenTextures( 1, &nTexID_out );
glBindTexture( GL_TEXTURE_2D, nTexID_out );
glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR );
// Create Frame buffer object and attach destination texture for drawing
glGenFramebuffersEXT( 1, &nFbo);
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, nFbo);
glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D, nTexID_out, 0 );
// Get the current view port to restore back after drawing
GLint viewport[4];
glGetIntegerv( GL_VIEWPORT, viewport );
// Prepare to draw (map) source texture to destination texture
glViewport( 0, 0, width, height );
glBindTexture( GL_TEXTURE_2D , nTexID_in );
// Start drawing
glClear( GL_COLOR_BUFFER_BIT );
glBegin( GL_POLYGON );
glTexCoord2f( 0, 0 ); glVertex2f( -1,-1 );
glTexCoord2f( 0, 1 ); glVertex2f( -1, 1 );
glTexCoord2f( 1, 1 ); glVertex2f( 1, 1 );
glTexCoord2f( 1, 0 ); glVertex2f( 1,-1 );
glEnd();
// unbind texture and frame buffer
glBindTexture( GL_TEXTURE_2D , 0 );
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
glDeleteFramebuffersEXT( GL_FRAMEBUFFER_EXT, &nFbo );
// Restore the old view port
glViewport( viewport[0],viewport[1],viewport[2],viewport[3]);
return nTexID_out;
}
[/sourcecode]
You can also use pixel buffer objects for copying images. But I feel not to include in this post to keep it as simple and minimal