The first thing you need is an OpenGL texture object. Say its identifier (as returned by glGenTextures() for example) is tid. Then, the second thing you have to code is a function to draw the texture at fullscreen with some rotation and scale, some sort of renderQuad(). This function assumes that both projection and modelview matrices are set to identity, so that the screen coordinates range from -1 to 1 both horizontal and vertically, in 2d.
void renderQuad( float time, int tid )
{
const float s = 0.8f + 0.3f*sinf(0.2f*time);
const float a = 0.4f*time;
glEnable( GL_TEXTURE_2D );
glBindTexture( GL_TEXTURE_2D, tid );
glBegin( GL_QUADS );
glTexCoord2f( 0.5f + s*cosf(a + 0.0000f), 0.5f + s*sinf(a + 0.0000f) ); glVertex2i( -1, 1 );
glTexCoord2f( 0.5f + s*cosf(a + 1.5708f), 0.5f + s*sinf(a + 1.5708f) ); glVertex2i( -1, -1 );
glTexCoord2f( 0.5f + s*cosf(a + 3.1416f), 0.5f + s*sinf(a + 3.1416f) ); glVertex2i( 1, -1 );
glTexCoord2f( 0.5f + s*cosf(a + 2.3562f), 0.5f + s*sinf(a + 2.3562f) ); glVertex2i( 1, 1 );
glEnd();
glBindTexture( GL_TEXTURE_2D, 0 );
glDisable( GL_TEXTURE_2D );
}
Good, believe it or not, we are almost done with the effect :) Now we need to introduce the feedback. To do so, what we are going to do is to take the buffer we just rendered with renderQuad() and copy it back to the texture we are using. But we are not going to completelly overwrite it, we will just update a part of the texture, like for example the center part. We will keep few pixels non updated on the border, just like when you film your television you camera is not fullscreen capturing the screen of the tv (the screen only covers part of the viewport). We can use the glCopyTexSubImage2D() for that, or we can use FBO (frame buffers objects). But let's do it in the old way, with glCopyTexSubImage2D():
void copyTexture( int textureSize, int border, int tid )
{
glBindTexture( GL_TEXTURE_2D, tid );
glCopyTexSubImage2D( GL_TEXTURE_2D, 0, border, border, 0, 0, textureSize-2*border, textureSize-2*border );
glBindTexture( GL_TEXTURE_2D, 0 );
}
The first argument is the size of the texture we are using, for example 512, and the second one, border, is the amount of pixels we are going to keep free of feedback,
for example, 64.Now we are ready to build the complete effect:
void renderFeedBackEffect( const MyEffect *ffx, float time, int xres, int yres )
{
// render a rotated and scaled version of the texture
glViewport( 0, 0, ffx->mTextureSize, ffx->mTextureSize );
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
glColor3f( 0.75f, 0.75f, 0.75f );
renderQuad( time, ffx->mTextureID );
// feedback
copyTexture( ffx->mTextureSize, 64, ffx->mTextureID );
// display the texture on screen
glViewport( 0, 0, xres, yres );
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
renderQuad( time, ffx->mTextureID );
}
The glColor3f() command is used so that each copy of the image in the feedback loop get's attenuated. That makes the old copies of the image fade to black slowly as
they get older (look the sample image on the top right of this page, note how the spirals go to dark). The glTexEnvi() is used to ensure this color modulation happens
in the right momments. Of course, xres and yres are the screen resolutions, and time is the current time for the frame (in seconds normally).Of course, do not forget to set the projection and modelview matrices to identity (no need for glOrtho() or anything), and disable depth tests. |