PDA

View Full Version : Motion Blur (copying the content of frame buffer to a texture)



proof88
02-09-2006, 01:11 PM
Hi!

A tried to make a motion blur-like effect and i decided to do this by drawing the scene to the color buffer then copying the content of the color buffer to a texture and finally switching to ortho mode and draw a quad that is textured with the texture and has a blending effect. But I have a problem: whenever i try to copy the content of the color buffer to the texture it does nothing, the texture stays completely black. I tried to play with the pixel storing modes and etc... but nothing helped.

Here are quotes form da source code (Delphi):
initializing gl:

ZeroMemory(@pfd, sizeof(pfd));
pfd.nSize := sizeof(pfd);
pfd.nVersion := 1;
pfd.dwFlags := PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL or PFD_DOUBLEBUFFER
or PFD_GENERIC_ACCELERATED;
pfd.iPixelType := PFD_TYPE_RGBA;
pfd.cColorBits := 32;
pfd.cDepthBits := 16;
pfd.iLayerType := PFD_MAIN_PLANE;
iFormat := ChoosePixelFormat(dc, @pfd);
SetPixelFormat(dc, iFormat, @pfd);
rc := wglCreateContext( dc );
wglMakeCurrent(dc,rc);
glShadeModel(GL_SMOOTH);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClearDepth(1.0); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_TEXTURE_2D);
CreateBlurTexture;The createblurtexture procedure makes the texture where the color buffer is copied @ everyframe to.


procedure CreateBlurTexture;
var pData: Pointer;
begin
GetMem(pData, 256*256*3);
glGenTextures(1, g_game.BlurTex);
glPixelstorei(GL_UNPACK_ALIGNMENT,3);
glBindTexture(GL_TEXTURE_2D, g_game.BlurTex);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTexImage2D(GL_TEXTURE_2D, 0, 3, 256, 256, 0, GL_RGB, GL_UNSIGNED_BYTE, pData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
FreeMem(pData);
end;and here is the rendering procedure:


begin
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
frm_game.FormResize(frm_game);
glTranslatef(0.0,0.0,-8.0);

glBegin(GL_QUADS);
glColor3f(1.0,0.0,0.0);
glNormal3f( 0.0, 0.0, 1.0);
glVertex2f(labda.x-labda.w/2,labda.y+labda.h/2);
glVertex2f(labda.x+labda.w/2,labda.y+labda.h/2);
glVertex2f(labda.x+labda.w/2,labda.y-labda.h/2);
glVertex2f(labda.x-labda.w/2,labda.y-labda.h/2);
glColor3f(1.0,1.0,1.0);
glNormal3f( 0.0, 0.0, 1.0);
glVertex2f(uto1.x-uto1.w/2,uto1.y+uto1.h/2);
glVertex2f(uto1.x+uto1.w/2,uto1.y+uto1.h/2);
glVertex2f(uto1.x+uto1.w/2,uto1.y-uto1.h/2);
glVertex2f(uto1.x-uto1.w/2,uto1.y-uto1.h/2);
glNormal3f( 0.0, 0.0, 1.0);
glVertex2f(uto2.x-uto1.w/2,uto2.y+uto2.h/2);
glVertex2f(uto2.x+uto1.w/2,uto2.y+uto2.h/2);
glVertex2f(uto2.x+uto1.w/2,uto2.y-uto2.h/2);
glVertex2f(uto2.x-uto1.w/2,uto2.y-uto2.h/2);
glEnd;

glBindTexture(GL_TEXTURE_2D,BlurTex);
glCopyTexSubImage2D(GL_TEXTURE_2D,0,0,0,0,0,255,25 5);

// Motion Blur code
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0,1.0,0,1.0,-1,1);

// glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_DEPTH_TEST);
// glEnable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D,BlurTex);

glBegin(GL_QUADS);
glTexCoord2f(0,0);
glVertex2f(0,0);
glTexCoord2f(1,0);
glVertex2f(0.5,0);
glTexCoord2f(1,1);
glVertex2f(0.5,0.5);
glTexCoord2f(0,1);
glVertex2f(0,0.5);
glEnd();

{ }

SwapBuffers(dc);

// glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
end; And sorry for my english. I'm looking forward to reading your replies! :)

jide
02-09-2006, 02:14 PM
The textures you use must be power of two units.
Also when you use:
glTexImage2D give a null value for the last argument and don't forget the internal format.


glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, 256, 256, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
glCopyTexSubImage2D (GL_TEXTURE_2D,0,0,0,0,0,256,256);might help.
The other things seems okay for me.

proof88
02-10-2006, 12:12 PM
Hi!

I tried that but the texture is just a black nothing... What do you think about it?

jide
02-11-2006, 12:28 AM
This time I had a look at the whole code.

First of all, are you sure you can see something with commenting your texture code ?
Then, what are your intentions ? do you want to see only the textured quad ?

Here are the other strange things I found:

1. you don't use any glViewport at all.
2. it seems you draw your colored quads in projection mode (this should be done in the modelview matrix). You do it this way because you call the projection matrix last in the rendering code but don't go back to modelview at all.
3. Call to projection matrix first, then modelview matrix.
4. Look then if your polygons are front facing the view.
5. calling glTranslatef each time without updating the modelview matrix will provoke a translation each time.

Some points might be wrong because I don't know what that function does:

frm_game.FormResize(frm_game);

So, here is what I could have done, but don't simply paste that code, it might still not work, ensure some points at least like if the view can see something:


begin
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);

// well maybe this code updates the view ?
// frm_game.FormResize(frm_game);

glViewport (0,0,256,256);
glMatrixMode (GL_PROJECTION);
glLoadIdentity();
gluPerspective (60, 4./3., 1., 20.);
glMatrixMode (GL_MODELVIEW);
glLoadIdentity();

glTranslatef(0.0,0.0,-8.0);

glBegin(GL_QUADS);
glColor3f(1.0,0.0,0.0);
glNormal3f( 0.0, 0.0, 1.0);
glVertex2f(labda.x-labda.w/2,labda.y+labda.h/2);
glVertex2f(labda.x+labda.w/2,labda.y+labda.h/2);
glVertex2f(labda.x+labda.w/2,labda.y-labda.h/2);
glVertex2f(labda.x-labda.w/2,labda.y-labda.h/2);
glColor3f(1.0,1.0,1.0);
glNormal3f( 0.0, 0.0, 1.0);
glVertex2f(uto1.x-uto1.w/2,uto1.y+uto1.h/2);
glVertex2f(uto1.x+uto1.w/2,uto1.y+uto1.h/2);
glVertex2f(uto1.x+uto1.w/2,uto1.y-uto1.h/2);
glVertex2f(uto1.x-uto1.w/2,uto1.y-uto1.h/2);
glNormal3f( 0.0, 0.0, 1.0);
glVertex2f(uto2.x-uto1.w/2,uto2.y+uto2.h/2);
glVertex2f(uto2.x+uto1.w/2,uto2.y+uto2.h/2);
glVertex2f(uto2.x+uto1.w/2,uto2.y-uto2.h/2);
glVertex2f(uto2.x-uto1.w/2,uto2.y-uto2.h/2);
glEnd;

glBindTexture(GL_TEXTURE_2D,BlurTex);
glCopyTexSubImage2D(GL_TEXTURE_2D,0,0,0,0,0,256,25 6);

// Motion Blur code

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0,1.0,0,1.0,-1,1);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

glViewport (0,0,w,h); // window width and height

glDisable(GL_DEPTH_TEST);

glBindTexture(GL_TEXTURE_2D,BlurTex);

glBegin(GL_QUADS);
glTexCoord2f(0,0);
glVertex2f(0,0);
glTexCoord2f(1,0);
glVertex2f(0.5,0);
glTexCoord2f(1,1);
glVertex2f(0.5,0.5);
glTexCoord2f(0,1);
glVertex2f(0,0.5);
glEnd();

{ }

SwapBuffers(dc);

glEnable(GL_DEPTH_TEST);
end;

proof88
02-11-2006, 04:03 AM
Hi!

Uhh, sorry about the formresize procedure!

It looks like this:


procedure Tfrm_game.FormResize(Sender: TObject);
begin
g_game.ResizeViewPort(256,256);
end;

procedure TGame.ResizeViewPort(w,h: GLuint);
begin
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, w/h, 1.0, 100.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
end;Well, i tried your code but the textured quad is just white. It faces to the camera.
My intention is to draw the 3 quads that are always moving, grab the content of the color buffer to a texture called BlurTex and finally draw a 4th quad in ortho mode above the other quads. This quad will be alpha-blended but texturing still doesn't work. I would like to texture this quad with BlurTex.

I modified the last bit of the rendering code just to test if i can color the 4th quad:


glBegin(GL_QUADS);
glcolor3f(1.0,1.0,0.0); //glTexCoord2f(0,0);
glVertex2f(0,0);
glcolor3f(1.0,1.0,0.0); //glTexCoord2f(1,0);
glVertex2f(0.5,0);
glcolor3f(1.0,0.0,0.0); //glTexCoord2f(1,1);
glVertex2f(0.5,0.5);
glcolor3f(1.0,0.0,0.0); //glTexCoord2f(0,1);
glVertex2f(0,0.5);
glEnd();And yes, it makes a cool gradient from yellow to red. So gltexcoord should be work.
I tried running the application from gDEBugger and looked at the texture and gDEBugger showed the BlurTex texture as a black picture so i think the problem is with glCopyTexSubImage2D(GL_TEXTURE_2D,0,0,0,0,0,256,25 6)
, maybe. That's why i thought that i was wrong with selecting the pixel storing and the color components.

jide
02-11-2006, 05:49 AM
Okay, here is what you should do:

. Don't use the depth buffer as you only do 2D stuff (so disable it everywhere)
. Use a unic square viewport with your texture size (256*256)
. Set your projection matrix and your modelview as this:

glMatrixMode (GL_PROJECTION);
glLoadIdentity();
gluOrtho2D (0,0,256,256);
glMatrixMode (GL_MODELVIEW);
glLoadIdentity();Now for the rendering routine:

. clear your color buffer
. Set your transformation views (don't forget to push the modelview matrix !)
. Draw your colored moving quads
. Bind your texture
. Set your texture env
. Copy the buffer to your texture
. Enable depth test with depth func set to GL_ONE,GL_ONE
. Draw your textured quad
. Disable depth test

This must work like this.

proof88
02-15-2006, 09:09 AM
Hi!

I modified the code as you suggested.
It looks like this:

procedure TGame.RenderScene;
begin
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);

glViewport (0,0,256,256);
glMatrixMode (GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, 1/1, 1.0, 20.0);
// this makes things not visible: gluOrtho2D (0,0,256,256);
glMatrixMode (GL_MODELVIEW);
glLoadIdentity();

glPushMatrix();
glTranslatef(0.0,0.0,-8.0);

glBegin(GL_QUADS);
glColor3f(1.0,0.0,0.0);
glNormal3f( 0.0, 0.0, 1.0);
glVertex2f(labda.x-labda.w/2,labda.y+labda.h/2);
glVertex2f(labda.x+labda.w/2,labda.y+labda.h/2);
glVertex2f(labda.x+labda.w/2,labda.y-labda.h/2);
glVertex2f(labda.x-labda.w/2,labda.y-labda.h/2);
glColor3f(1.0,1.0,1.0);
glNormal3f( 0.0, 0.0, 1.0);
glVertex2f(uto1.x-uto1.w/2,uto1.y+uto1.h/2);
glVertex2f(uto1.x+uto1.w/2,uto1.y+uto1.h/2);
glVertex2f(uto1.x+uto1.w/2,uto1.y-uto1.h/2);
glVertex2f(uto1.x-uto1.w/2,uto1.y-uto1.h/2);
glNormal3f( 0.0, 0.0, 1.0);
glVertex2f(uto2.x-uto1.w/2,uto2.y+uto2.h/2);
glVertex2f(uto2.x+uto1.w/2,uto2.y+uto2.h/2);
glVertex2f(uto2.x+uto1.w/2,uto2.y-uto2.h/2);
glVertex2f(uto2.x-uto1.w/2,uto2.y-uto2.h/2);
glEnd;

glpopmatrix();

glBindTexture(GL_TEXTURE_2D,BlurTex);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glCopyTexSubImage2D(GL_TEXTURE_2D,0,0,0,0,0,256,25 6);

glenable(gl_depth_test);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D (0,0,256,256);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

glrotatef(45,0,1,0);

glBegin(GL_QUADS);
glTexCoord2f(0,0);
glVertex2f(0,0);
glTexCoord2f(1,0);
glVertex2f(0.5,0);
glTexCoord2f(1,1);
glVertex2f(0.5,0.5);
glTexCoord2f(0,1);
glVertex2f(0,0.5);
glEnd();


{ }

SwapBuffers(dc);
end;It still doesn't work. You wrote that i should set depthfunc to gl_one,gl_one. Do you mean to set Blendfunc to gl_one,gl_one?
I'm trying to figure out what am i doing wrong. I have an example program with source code and it makes motion blur like i want to do but that works and mine doesn't.

jide
02-15-2006, 09:36 AM
Yes that was I'd like to mean (glBlendFunc) sorry for that point.

I made a little test and it worked for me (the texture is seenable). Here it is:


GLuint tex_id;

void init()
{
glClearColor (0.,0.,0.,0.);
glShadeModel (GL_SMOOTH);
glDisable (GL_CULL_FACE);

glGenTextures(1, &tex_id);
glBindTexture(GL_TEXTURE_2D, tex_id);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 256, 256, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
}

void reshape (int w, int h)
{
glViewport (0,0,w,h);
glMatrixMode (GL_PROJECTION);
glLoadIdentity();
glOrtho (-5,5,-5,5,-5,5);
glMatrixMode (GL_MODELVIEW);
glLoadIdentity();
}

void
display()
{
glClear (GL_COLOR_BUFFER_BIT);

glPushMatrix();
glTranslatef (0,0,5);
glColor3f (1.0f, 0.0f, 0.0f);
glBegin( GL_TRIANGLES);
glVertex2f(-3.0,0.0);
glVertex2f(3.0,0.0);
glVertex2f(3.0,3.0);
glVertex2f(-3.0,3.0);
glVertex2f(-3.0,0.0);
glVertex2f(3.0,3.0);
glEnd();

glEnable (GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,tex_id);
glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glCopyTexSubImage2D(GL_TEXTURE_2D,0,0,0,0,0,256,25 6);

glColor3f (0,1,0);
glEnable (GL_BLEND);
glBlendFunc (GL_ONE,GL_ONE);
glBegin(GL_TRIANGLES);
glTexCoord2f (0,0);
glVertex2f( -1.0,0.0);
glTexCoord2f (1,0);
glVertex2f( 5.0,0.0);
glTexCoord2f (1,1);
glVertex2f( 5.0,3.0);
glTexCoord2f (0,1);
glVertex2f( -1.0,3.0);
glTexCoord2f (0,0);
glVertex2f( -1.0,0.0);
glTexCoord2f (1,1);
glVertex2f( 5.0,3.0);
glEnd();
glDisable (GL_BLEND);
glDisable (GL_TEXTURE_2D);

glPopMatrix();
glutSwapBuffers();
glutPostRedisplay();
}

void KeyFunc (unsigned char key, int x, int y)
{
switch (key){
case 27:
exit (0);
}
}

void idle (void)
{
glutPostRedisplay();
}

int main (int argc, char**argv)
{
glutInit (&argc, argv);
glutInitWindowPosition (0,0);
glutInitWindowSize (256,256);
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA);
glutCreateWindow ("quads");

init();

glutDisplayFunc (display);
glutReshapeFunc (reshape);
glutKeyboardFunc (KeyFunc);
glutMainLoop();

return 0;
}Cannot make more.

zerked
02-18-2006, 01:06 AM
not exactly a solution to you're problem, but just another easy way of doing motion blur by using the accumulation buffer :)

here we go:

"The motion blur is implemented by keeping an accumulation of previously blurred pictures and then for each frame, scale down the current accumulation slightly and add the new frame. The accumulation buffer is perfect for this task, and itís achieved with only three lines of code:" - ATI

Using the accumulation buffer isn't the fastest way possible, but the really cool thing about motion blur is that when your whole scene is blurred you really can't tell the difference between 100fps or 15fps. Now if you're trying to just keep one object blured throughout the life span of the application than you probably want to avoid this method.

To enable the accumulation buffer you're going to need to set it to true in the pixel format descriptor like so:

PIXELFORMATDESCRIPTOR pfd = {
sizeof ( PIXELFORMATDESCRIPTOR ),
1,
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
32,
0, 0, 0, 0, 0, 0,
0, 0,
1, // Enable the accumulation buffer
0, 0, 0, 0,
16,
0, 0, 0, 0, 0
}; Once that is done you're going to want to intialize glAccum:

// Initialize AB
glAccum(GL_LOAD, 1); Now for the most tricky part(not really that tricky :) ) You're going to want to do the following after drawing all you're 3D objects:


float bf = 0.75f
if (bf > 0){
bf = powf(bf, 50 * m_fElapsed);

// Multiply the accumulation buffer with a constant less than 1
glAccum(GL_MULT, bf);
// Add the framebuffer contents scaled down
glAccum(GL_ACCUM, 1.0f - bf);

// Transfer back to framebuffer
glAccum(GL_RETURN, 1);
} If for some reason this doesn't work for you, you can always use glError() to find out what the problem is - there is plenty of papers online about using the AB to do motion blur, here a few websites:

openGL doc (http://www.opengl.org/resources/tutorials/advanced/advanced96/node39.html#SECTION000101000000000000000)
GL Refrence (http://pyopengl.sourceforge.net/documentation/manual/glAccum.3G.xml)