GLSL Motion Blur

Started by
10 comments, last by Mctittles 12 years, 9 months ago
Hey everyone! I've been looking pretty hard for some example code of a GLSL shader used for motion blur. I don't need anything more complex than a per-frame motion blur, but I can't seem to find any GLSL-specific code. Does anyone know where I can find a tutorial or example code to start with? The end result I want is an exaggerated blur along the path of a projectile. I would really like to learn how to do this, and not just rip code, but I'm having a very hard time getting started... Thanks ahead of time!
Advertisement
pass 1:

// this creates the motion-vectors. Render the whole scene with this shader#include "system.h"varying vec2 texcoord0 :TEX0;varying vec4 curpos :TEX1;varying vec4 prevpos:TEX2;#if IS_VERTEXuniform mat4 matcur;uniform mat4 matprev;void main(){	texcoord0=gl_MultiTexCoord0.xy;	curpos  = matcur  * gl_Vertex;	prevpos = matprev * gl_Vertex;	gl_Position=curpos;}#endif#if IS_FRAGMENTvoid main(){	vec2 position  = curpos.xy/curpos.w;	vec2 position2 = prevpos.xy/prevpos.w;	#define BLUR_VECTOR_SCALE 60.0		vec2 delta = (position-position2) * (SCREEN_SIZE/BLUR_VECTOR_SCALE) +0.5;		float DOF_blur = abs(curpos.z/curpos.w - 0.8);		gl_FragColor = vec4(delta,DOF_blur,0);}#endif


pass 2:
// this is the final, post-processing step to do motion-blur#include "system.h"#define FAST_PRECISION//varying vec2 texcoord0;#define texcoord0 gl_TexCoord[0].xy#if IS_VERTEXvoid main(){	texcoord0=gl_MultiTexCoord0.xy;	gl_Position=gl_Vertex;}#endif#if IS_FRAGMENTuniform samplerRect tex0;uniform samplerRect tex1;uniform samplerRect tex2;const vec2 poisson[16]={	vec2(0.007937789, 0.73124397),	vec2(-0.10177308, -0.6509396),	vec2(-0.9906806, -0.63400936),	vec2(-0.5583586, -0.3614012),	vec2(0.7163085, 0.22836149),	vec2(-0.65210974, 0.37117887),	vec2(-0.12714535, 0.112056136),	vec2(0.48898065, -0.66669613),	vec2(-0.9744036, 0.9155904),	vec2(0.9274436, -0.9896486),	vec2(0.9782181, 0.90990245),	vec2(0.96427417, -0.25506377),	vec2(-0.5021933, -0.9712455),	vec2(0.3091557, -0.17652994),	vec2(0.4665941, 0.96454906),	vec2(-0.461774, 0.9360856)};void main(){	const float numSamples=8;	#define BLUR_VECTOR_SCALE 60.0	vec2 vector = (textureRect(tex0,texcoord0).xy-0.5) * BLUR_VECTOR_SCALE/numSamples;	vec4 color=0;	/*	//------------[ Depth of Field ]---------------------[	#if 0	float radius = textureRect(tex1,texcoord0).z * 30;		for(int i=0;i<16;i++){		color+= textureRect(tex1,texcoord0+ radius*poisson);	}	color/=16;	gl_FragColor = color+ vector.xyxy*0.00001;	#else	float radius = (textureRect(tex1,texcoord0).z+0.01) * 3;	#define SIZ 1	#define SIZ2 (SIZ*2+1)	#define SIZ3 (SIZ2*SIZ2)	for(int y=-SIZ;y<=SIZ;y++){		for(int x=-SIZ;x<=SIZ;x++){			vec2 v = vec2(x,y);			float len=length(v)+1;			color+=textureRect(tex1,texcoord0+ radius*v)/len;		}	}		//color/=SIZ3;	color/=20;		gl_FragColor = color+ vector.xyxy*0.00001;	#endif	//---------------------------------------------------/	*/		#define METHOD 1	#if METHOD==1	for(float i=0;i<numSamples/2;i++){		color+= textureRect(tex1,texcoord0+vector*i);		color+= textureRect(tex2,texcoord0+vector*i);	}	for(float i=numSamples/2;i<numSamples;i++){		color+= textureRect(tex1,texcoord0+vector*i)*0.5;		color+= textureRect(tex2,texcoord0+vector*i)*0.5;	}			for(float i=0;i<numSamples/2;i++){		color+= textureRect(tex1,texcoord0-vector*i);		color+= textureRect(tex2,texcoord0-vector*i);	}	for(float i=numSamples/2;i<numSamples;i++){		color+= textureRect(tex1,texcoord0-vector*i)*0.5;		color+= textureRect(tex2,texcoord0-vector*i)*0.5;	}	gl_FragColor = color/(numSamples*3);#endif#if METHOD==2	for(float i=0;i<numSamples/2;i++){		color+= textureRect(tex1,texcoord0+vector*i);	}		for(float i=numSamples/2;i<numSamples;i++){		color+= textureRect(tex1,texcoord0+vector*i)*0.5;	}		for(float i=0;i<numSamples/2;i++){		color+= textureRect(tex1,texcoord0-vector*i);	}	for(float i=numSamples/2;i<numSamples;i++){		color+= textureRect(tex1,texcoord0-vector*i)*0.5;	}		gl_FragColor = color/(numSamples * 1.5);	#endif#if METHOD==3	for(float i=0;i<numSamples;i++){		color+= textureRect(tex1,texcoord0+vector*i);	}	gl_FragColor = color/numSamples;	#endif#if METHOD==4	for(float i=0;i<numSamples;i++){		vec4 color1 = textureRect(tex1,texcoord0-vector*i);		vec4 color2 = textureRect(tex2,texcoord0-vector*i)*0.01;		float alpha = i/(numSamples);				color += mix(color1,color2,alpha);	}	for(float i=1;i<numSamples;i++){		vec4 color1 = textureRect(tex1,texcoord0+vector*i);		vec4 color2 = textureRect(tex2,texcoord0+vector*i)*0.01;				float alpha = i/(numSamples);				color += mix(color1,color2,alpha);	}		gl_FragColor = color/(numSamples);	#endif					}#endif


The pass 1 is not a post-process, requires redrawing the scene with this shader. I haven't implemented this pass as a post-process yet.
In real life, a motion blur happens because an object moved in the frame while the shutter was open. In OpenGL, this can be simulated by averaging consecutive frames together.

Capture a certain number of frames (say 5) and save them into 5 textures, which are continually updated, with the current rendered scene being copied into the oldest texture.

When you want to draw onto the screen, you draw a fullscreen quad, multitextured with all 5 textures. Just average 5 samples (1 from each texture) to get the effect.

This will create a fullscreen motion blur. You can get a more local scale version with a bit more effort, only updating the areas you want blurred.

[size="1"]And a Unix user said rm -rf *.* and all was null and void...|There's no place like 127.0.0.1|The Application "Programmer" has unexpectedly quit. An error of type A.M. has occurred.
[size="2"]

Wow, this will certainly help! Thanks very much to both of you!

I've read through a good chunk of the shader code, primarily focusing on the first pass, and I don't really understand this part:

vec2 position  = curpos.xy/curpos.w;vec2 position2 = prevpos.xy/prevpos.w;


I also saw division by the w component a few lines down. What does dividing by the w component of the vector achieve?
Does perspective divide... giving screen-space coords out of the clipspace coords.

The method, proposed by Geometrian is rarely used in games, even though it's extremely easy to implement. For a reason.
idinev: I'm going to be showcasing my game in a couple months, would you mind if I put you in the credits? If you would like to be in the credits, is this the name you would like put in, or is there another alias you would prefer?
Thanks; my primary nickname is "Ultrano".
Cool stuff. Thanks again, chief!
Sorry to bring back an old thread but this looks like exactly what I need for my program and I'm having a difficult time understanding the code. A few things are confusing or do not work in GLSL.

#include "system.h"
Can you include files in shaders? If so, is this a file I need to have in order for it to work?

What variables do I need to send to the shader in order for it to work?

[color="#1C2837"]IS_VERTEX
[color="#1C2837"]I've never seen this before, does this check if it's vertex/fragment and should I be using the same shader for both or can I split them into two shaders?
Thanks for any help on this!

Sorry to bring back an old thread but this looks like exactly what I need for my program and I'm having a difficult time understanding the code. A few things are confusing or do not work in GLSL.

#include "system.h"
Can you include files in shaders? If so, is this a file I need to have in order for it to work?



Yes, you can and yes, you do.


What variables do I need to send to the shader in order for it to work?
[/quote]

See a list of uniforms for fragment shader in pass 2.


[color="#1C2837"]IS_VERTEX
[color="#1C2837"]I've never seen this before, does this check if it's vertex/fragment and should I be using the same shader for both or can I split them into two shaders?
Thanks for any help on this!
[/quote]

No, it does not check per se, it's not GLSL standard constant or something. It's probably just additional preprocessor symbol idinev defines somewhere before compilation (like adds it to shader code programmaticaly before compilation). Generally, this is the only way of "communication" between your program code and your shader code. You can just extract the code between #if IS_VERTEX .. #endif to one file and the code between #if IS_FRAGMENT .. #endif to another file and use two shader files, but they still won't work out of the box anyway.

Generally, if you can't get a grip of these things, you should carefully study GLSL specs and find a good book on C or C++. GLSL specs provide some explanations e.g. for preprocessor, but from a perspective that you already know the works.
WBW, capricorn

This topic is closed to new replies.

Advertisement