Motion Blur

BlitzMax Forums/BlitzMax Beginners Area/Motion Blur

Ked(Posted 2007) [#1]
Does anyone have a code example of creating a motion blur overlay effect?

Thanks


xlsior(Posted 2007) [#2]
I guess you could just draw the sprite multiple times at incremental alpha transparancies towards the direction of the motion (so you see one sharp solid object, and some faded smears in the direction it came from)


Ked(Posted 2007) [#3]
Would it work to copy everything within GraphicsWidth() and GraphicsHeight() into an image and draw with 0.75 alpha? If this is possible could someone show me how?


Raph(Posted 2007) [#4]
If you want fullscreen motion blur, just replace your Cls call with a full screen DrawRect drawn with alpha. For example, a black rect that is full screen at .1 alpha will take ten frames to clear a given pixel.


xlsior(Posted 2007) [#5]
If you want fullscreen motion blur, just replace your Cls call with a full screen DrawRect drawn with alpha. For example, a black rect that is full screen at .1 alpha will take ten frames to clear a given pixel.


Except that you can't count on wha tthe screen will look like without clearing it: some video adapters double-buffer, other tripple- or even quad buffer, meaning that your 'old' grpahicsd may jump back and forth or flash between old content and a black screen. You may even see a 'random' memory dump flash by.

If you want to re-use/modify existing image data, you will *have* to redraw it to ensure it shows up correctly on all systems.


xlsior(Posted 2007) [#6]
Here's a quick and dirty example for giving motion blur to a specific sprite... 5 different steps, or for a smoother appearance average out the in-between steps for 9 total. Move the mouse to see the blur. Hold down the left mouse button to enable the additional in-between steps.

SuperStrict
Graphics 1024,768
Local backgroundimg:Timage=LoadImage("j:\monkey4.jpg")
Local spriteimg:Timage=LoadImage("C:\Code\BlitzMax\samples\digesteroids\graphics\Digestive.png")

Global oldcoord:Int[5,2] ' Array with 5 slots to remember old positions. [n,0]=X, [n,1]=Y
Local t:Int				' Temporary counter variable
Local Average:Int			' Whether or not we want to smooth the movements.

For t=0 To 4
	'We have an array of 5 positions (0 through 4) which hold the previous locations of the sprite
	'that we want to follow. first pre-populate all slots to the beginning position of the sprite,
	'so that we start out with no trail. using mousex and mousey since those are the sprite position.
	oldcoord[t,0]=MouseX()
	oldcoord[t,1]=MouseY()
Next

SetBlend alphablend
DrawImage(backgroundimg:Timage,0,0)

While Not KeyDown(key_escape)
	For t=3 To 0 Step -1
		' Move the old positions each one spot down the line in the array, overwriting the last spot
		oldcoord[t+1,0]=oldcoord[t,0]
		oldcoord[t+1,1]=oldcoord[t,1]
	Next
	oldcoord[0,0]=MouseX()	' and store the current position at the first slot
	oldcoord[0,1]=MouseY()	
	SetAlpha 1.0								' Set the alpha to full
	DrawImage(backgroundimg:timage,0,0)		' draw the current background image

	SetAlpha 0.2	' Set the alpha to 0.2 for the final, weakest copy in the trail, and draw at the
					' oldest remembered coordinates
	DrawImage(spriteimg:timage,oldcoord[4,0],oldcoord[4,1])

	If average=True Then	' If average is enabled, then draw an intermediate fade on the location
		SetAlpha 0.3		' in between the oldest and second-oldest locations
		DrawImage(spriteimg:timage,(oldcoord[4,0]+oldcoord[3,0])/2,(oldcoord[4,1]+oldcoord[3,1])/2)
	End If
	SetAlpha 0.4		' draw a stronger copy on the next position in line
	DrawImage(spriteimg:timage,oldcoord[3,0],oldcoord[3,1])

	If average=True Then	' ETC.
		SetAlpha 0.5
		DrawImage(spriteimg:timage,(oldcoord[3,0]+oldcoord[2,0])/2,(oldcoord[3,1]+oldcoord[2,1])/2)
	End If
	SetAlpha 0.6
	DrawImage(spriteimg:timage,oldcoord[2,0],oldcoord[2,1])
	If average=True Then
		SetAlpha 0.7
		DrawImage(spriteimg:timage,(oldcoord[2,0]+oldcoord[1,0])/2,(oldcoord[2,1]+oldcoord[1,1])/2)
	End If
	SetAlpha 0.8
	DrawImage(spriteimg:timage,oldcoord[1,0],oldcoord[1,1])
	If average=True Then
		SetAlpha 0.9
		DrawImage(spriteimg:timage,(oldcoord[1,0]+oldcoord[0,0])/2,(oldcoord[1,1]+oldcoord[0,1])/2)
	End If
	SetAlpha 1.0													' Set the alpha to full again and draw the
	DrawImage(spriteimg:timage,oldcoord[0,0],oldcoord[0,1])	' active sprite on top of the faded trail
	Flip	' Show everything

	If MouseDown(1) Then
		average=True		' Draw the averaged intermediate fades when the left mousebutton is held
	Else
		average=False		' Or don't draw them if the left mouse button is not pressed
	End If
Wend




ImaginaryHuman(Posted 2007) [#7]
Use the accumulation buffer in OpenGL, or grab the screen into a texture and then draw it with a 0.1 level of alpha in the next frame.


Ked(Posted 2007) [#8]
I used what Raph suggested and I like the way it looks. I haven't found anything wrong with it yet either.


REDi(Posted 2007) [#9]
As xlsior said, it might work fine for you, but some computers WILL have problems with it.


Mortiis(Posted 2007) [#10]
Use the accumulation buffer in OpenGL, or grab the screen into a texture and then draw it with a 0.1 level of alpha in the next frame


Which one is faster regarding performance?


xlsior(Posted 2007) [#11]
I used what Raph suggested and I like the way it looks. I haven't found anything wrong with it yet either.


Not on your computer, but many others will have issues -- you'll get out-of-order flashing back between screens on some computers (e.g. rather than always seeing the previous screen, you may alternate between 1, 2 and 3 screens ago giving a really annoying flickering effect) or you may flicker between the previous screen and a black screen, etc.

If you do want that method, you would need one more step to guarantee it works for everyone:

createimage(background:TImage,graphicswidth(), graphicsheight()) ' Create an image to hold the current screen in

setclscolor (0,0,0)
while not keydown(key_escape)
cls
SetAlpha 0.8
DrawImage (background:Timage,0,0)
SetAlpha 1.0
Drawimage MySprite,mousex(),mousey()' draw all your real current images now, and do the rest of your stuff
'
GrabImage(background:Timage,0,0) ' Store a copy of the current screen in its final state, so we can redraw it later at a lower alpha
flip
wend


This should look the same as what Ralph suggested, except that it will work on every computer since you make sure that you save and redraw the old content, rather than hoping that the video card saves it for you, and hoping that it only uses a single buffer to do so. The OpenGL and DX specs specifically state you cannot count on this being the case.


Ked(Posted 2007) [#12]
xlsior: Is that supposed to be really slow? Because on my computer there almost isn't any movement.


matt!(Posted 2011) [#13]
Thanks @xlsior for this!