GMS 2 Fullscreen Motion Blur

V

Vṛka Majjā

Guest
Hey, guys. I was wondering if anybody has some motion blur scripts or shaders to blur everything visible. I can script code to motion blur individual objects and it looks great, but I'd love to see it all blur.
 
I know this is really old, but I'm wondering the same thing. Am able to get motion blur for individual objects, but don't know how to apply it to the whole screen.
 

Tyg

Member
You can put a dummy object that covers the srceen then apply a shader or capture the video buffer then appy the shader...hold on ill dig some code up

function GetOldScreen() {
global.OldScreen = sprite_create_from_surface(application_surface,0,0,room_width,room_height,0,1,room_width/2,room_height/2);
}

this will turn your screen into a sprite

now it depends it you want a motion blur on a still or animated sprite or if you want to have a constanlty running motion blur as this will be differerent as to how you apply the shader

Put this in the draw event of the sprite

// Function Call
shader_set(Radial_Blur);
draw_self();
shader_reset();

now create a new shader and call it Radial_Blur

Here is the shader code, replace your code in your fsh part of the shader (it will have 2 tabs in the ide vsh and fsh)
----------------------------------------------

//
// Simple passthrough fragment shader
//
varying vec2 v_vTexcoord;
varying vec4 v_vColour;

void main()
{
vec4 O_color = (texture2D( gm_BaseTexture, v_vTexcoord )
+ texture2D( gm_BaseTexture, v_vTexcoord*0.99+0.5*0.01 )
+ texture2D( gm_BaseTexture, v_vTexcoord*0.98+0.5*0.02 )
+ texture2D( gm_BaseTexture, v_vTexcoord*0.97+0.5*0.03 )
+ texture2D( gm_BaseTexture, v_vTexcoord*0.96+0.5*0.04 )
+ texture2D( gm_BaseTexture, v_vTexcoord*0.95+0.5*0.05 )
+ texture2D( gm_BaseTexture, v_vTexcoord*0.94+0.5*0.06 )
+ texture2D( gm_BaseTexture, v_vTexcoord*0.93+0.5*0.07 )
+ texture2D( gm_BaseTexture, v_vTexcoord*0.92+0.5*0.08 )
+ texture2D( gm_BaseTexture, v_vTexcoord*0.91+0.5*0.09 )) * 0.1;

gl_FragColor = O_color * v_vColour;
}

------------------------------------------------
it basicly just repeats the texture at diff coordinates for a smear effect
enjoy and hope this helps :)

this is just a static blur a motion blur is just a repeated image with different layers of opacity
you could also make the text coords variables that would have to be passed in if you want the blur to move around or could use a time effect
 
Last edited:
Thanks @Tyg, for the in-depth explanation.

I will experiment with it and see what I get. Maybe OP can join in sometime lol. I assume though that the GetOldScreen function will need to run every step for as long as the blur is intended. Turning the screen into a sprite every tic is somewhat concerning regarding performance, but some tests should help clarify this issue.
 
Last edited:

Tyg

Member
It really depends on what effect you are going for
if you tell me what you are trying to do i could tell you the best way to do it a time var could be added to the shader for random blur or direction varibles
the getscreen just makes a sprite out of the screen and only need to be run once or not even if you just want a blurry sprite, if your background is constantly changing then
i would use a dummy sprite covering the screen layer and just put the shader to it..no getscreen needed
the shader can be applied to any sprite without the getscreen
if you want a constanty blurring screen it can be directly applied to the application surface

the shader is super fast as it goes directly to the gpu...the getscreen would be used for like a paused game...the dummy sprite is the best option for a moving background
 
Last edited:
I've justed tested it out and it works great!

However, the next issue is the one you just mentioned about using a dummy sprite for a constantly changing background. Right now, if the motion blur is used with the screen cap method, it works, but every tic means that it'd simply apply the shader over the image that was already captured a tic ago so it looks like its just doing the motion blur constantly on the first captured frame only over and over.

The goal is basically to get motion blur applied over the full screen during ongoing gameplay. So for example if we were to have a demolition car racing game, if say one of the cars blew up, make the effect look better by having a motion blur applied to the whole screen in addition to other effects like screen shaking, explosions, etc. Essentially have the motion blur happen while things are happening in the game. So going by your post, I believe using a dummy sprite covering the screen layer + apply shader method would be ideal. Could you elaborate on that though?

Here's what I have so far with the screen cap method (a bit sloppy and sprite_delete and other items not included since only testing):

For the screen cap object:
Create
GML:
sp_old = -1
Step
Code:
if keyboard_check(vk_left){
    sp_old = sprite_create_from_surface(application_surface,0,0,view_wport[0],view_hport[0],0,0,0,0);
    }
Draw
Code:
if(sprite_exists(sp_old))
    {
    shader_set(shd_mb);    //motion blur shader
    draw_sprite(sp_old, 0, 0, 0)
    shader_reset();
}
And the shader (shd_mb) is exactly the one you supplied earlier.

Thanks!

EDIT: small note, I'm still on GMS 2.2+, not transitioned to 2.3. Though I don't believe this will be an issue here.
 
Last edited:

Tyg

Member
Well if thats what your trying to do forget about the dummy sprite
use the shader directly on the application surface which can be enabled or disabled or modified directly

all you need to do is this

create a dummy object and throw it in the corner of the room (no sprite)

now in the draw event of the dummy object put this

-----------------------
shader_set(Radial_Blur);
draw_surface(application_surface, 0, 0);
shader_reset();
-----------------------

Now whenever the application surface is drawn the shader is applied to it...so even if the screen is scaled it works
if you want more control of the effect like just in one corner you can use draw_surface_ext
There is no need to turn the application surface off...if you want to disable the effect just use dummy_object.visible = false; which disables its draw event
That should do it...i tested it
:)

OOps..i need a little fix here
i had to use
draw_surface_ext(application_surface, 0, 0, 2, 1.5,0,0,1);
ill need to check the uv coordinates..when i do a capture i have to use room_width/2
ill update when tested
 
Last edited:

Tyg

Member
So if you wanted the whole screen to ripple
you would put this in your dummy_object

--------------------------------------------

-------------------
shader_set(Ripple);
uTime = shader_get_uniform(Ripple, "iTime");
shader_set_uniform_f(uTime, frac(current_time/1000));
draw_surface(application_surface, 0, 0);
shader_reset();
-------------------------

and here is the fsh

---------------------------
//
// Simple passthrough fragment shader
//
varying vec2 v_vTexcoord;
varying vec4 v_vColour;

uniform float iTime;
uniform vec2 iRez;

// Maximum number of cells a ripple can cross.
#define MAX_RADIUS 2

// Set to 1 to hash twice. Slower, but less patterns.
#define DOUBLE_HASH 0

#define HASHSCALE1 .1031
#define HASHSCALE3 vec3(.1031, .1030, .0973)

float hash12(vec2 p)
{
vec3 p3 = fract(vec3(p.xyx) * HASHSCALE1);
p3 += dot(p3, p3.yzx + 19.19);
return fract((p3.x + p3.y) * p3.z);
}

vec2 hash22(vec2 p)
{
vec3 p3 = fract(vec3(p.xyx) * HASHSCALE3);
p3 += dot(p3, p3.yzx+19.19);
return fract((p3.xx+p3.yz)*p3.zy);

}

void main()
{
vec4 O_Color = texture2D(gm_BaseTexture, v_vTexcoord );

//float resolution = 10. * exp2(-3.*iMouse.x/iResolution.x);
vec2 uv = v_vTexcoord.xy;
vec2 p0 = floor(uv);

vec2 circles = vec2(0.5,0.7);
for (int j = -MAX_RADIUS; j <= MAX_RADIUS; ++j)
{
for (int i = -MAX_RADIUS; i <= MAX_RADIUS; ++i)
{
vec2 pi = p0 + vec2(i, j);
#if DOUBLE_HASH
vec2 hsh = hash22(pi);
#else
vec2 hsh = pi;
#endif
vec2 p = pi + hash22(hsh);

float t = fract(0.3*iTime + hash12(hsh));
vec2 v = p - uv;
float d = length(v) - (float(MAX_RADIUS) + 1.)*t;

float h = 1e-3;
float d1 = d - h;
float d2 = d + h;
float p1 = sin(31.*d1) * smoothstep(-0.6, -0.3, d1) * smoothstep(0., -0.3, d1);
float p2 = sin(31.*d2) * smoothstep(-0.6, -0.3, d2) * smoothstep(0., -0.3, d2);
circles += 0.5 * normalize(v) * ((p2 - p1) / (2. * h) * (1. - t) * (1. - t));
}
}
circles /= float((MAX_RADIUS*2+1)*(MAX_RADIUS*2+1));

float intensity = mix(0.01, 0.15, smoothstep(0.1, 0.6, abs(fract(0.05*iTime + 0.5)*2.-1.)));
vec3 n = vec3(circles, sqrt(1. - dot(circles, circles)));
vec3 color = texture2D(gm_BaseTexture, uv - intensity*n.xy).rgb + 5.*pow(clamp(dot(n, normalize(vec3(1., 0.7, 0.5))), 0., 1.), 6.);
if (O_Color.a != 0.0){
gl_FragColor = vec4(color, 1.0);
}
}

--------------------------
 
Last edited:
Great stuff @Tyg !

The ripples shader sounds promising, but before I begin experimenting with it, there might be some further tinkering needed on the motion blur.

I've tried it and it works, but the results are somewhat unpredictable. The intention is to run the project on a 384 x 216 resolution, but scale the game window up to 1536 x 864 using the window_set_size function, while making sure the surface is resized to 384 width, 216 height.

The shader triggers fine and I can see the effect, but in the example I did, some items (red circles moving back and forth) couldn't be seen as motion blurred, but it works fine on the background. Additionally, in the shader code you provided in your first post, if I change the 0.9x values to 0.5x, the motion blur appears fine as far as the background goes (red circles not visible unfortunately). But if the values stay as 0.9x, I get some offset results where I'm seeing more than one surfaces with the motion blur.

Relevant code as follows:

Create:
GML:
depth = -99
sp_old = -1 //sprite_create_from_surface(application_surface,0,0,room_width,room_height,0,1,room_width/2,room_height/2);

vis = 0;
visible = vis

game_w_start = 1536
game_h_start = 864

display_set_gui_size(game_w_start, game_h_start); //fixed
window_set_size(game_w_start, game_h_start); //fixed

surface_resize(application_surface, game_w_start/4, game_h_start/4); //native resolution
Step:
Code:
if keyboard_check_pressed(vk_left){
    //sp_old = sprite_create_from_surface(application_surface,0,0,view_wport[0],view_hport[0],0,0,0,0);
    vis = !vis   
    }
    
visible = vis
Draw:
Code:
//if(sprite_exists(sp_old))
    {   
    shader_set(shd_mb);    //motion blur shader
    //draw_sprite(sp_old, 0, 0, 0)
    draw_surface(application_surface, 0, 0)
    shader_reset();
}
If it is convenient and of interest to you, I've attached the YYZ file of this example (GMS 2.2x so hope that won't be too problematic). It still retains the screen capture items from before, just commented out.

In any case, thanks for all your help so far!

Link: https://www.dropbox.com/s/k87hpzjknwsknmh/motion blur full screen.yyz?dl=0
 

Tyg

Member
Ok i see what your trying to do...your not using the entire room surface...so the shader will have to be applied differently
you only want the shader active when you move correct? its just a matter of me finding the right surface...ill work on it tonight..
 
Last edited:
Ah, an oversight on my part - apologies for that! Yes, the shader would ideally need to only work on whatever is visible in the view/screen at the moment, not the entire room (which would apply the blur differently I believe). The shader would trigger when a certain event happened. In this case, its the left key being pressed once to activate and again to deactivate - in the step event.

Thanks for your input! I'll see if I have something relevant regarding the view movement on my side.
 

Tyg

Member
Ok
i made a global.Moving var then on the bgm object the background put the shader there on the draw event if global.moving do the shader or regular draw if idle on an object the keydown left key would simply set global.moving to true...and when keyup left left would set it to false.
that way the shader ontriggers while moving
also i dont think you would want to blur any HUD or GUI items unless in a pause state
any easy way is to make a parent object with the shader could that turns on and off and then every cild of that object inherits the draw event
 

Tyg

Member
if your wanting a motion blur say on a player this is a cool effect from one of my games
Untitled.jpg
function vanish(argument0, argument1, argument2, argument3, argument4)
{
// If Variable
if(global.vanishing == false)
{
// Assign Variable
v_obj = argument0;
tx = argument1;
ty = argument2;
v_speed = argument3;
txs = v_obj.image_xscale/100;
tys = v_obj.image_yscale/100;
xstep = -(v_obj.x-tx)/100;
ystep = -(v_obj.y-ty)/100;
v_rot = argument4;

// Create Instance
instance_create_layer(v_obj.x, v_obj.y, "Instances", obj_vanish);

// Assign Variable
obj_vanish.sprite_index = v_obj.sprite_index;
obj_vanish.image_xscale = v_obj.image_xscale;
obj_vanish.image_yscale = v_obj.image_yscale;
obj_vanish.image_angle = v_obj.image_angle;

// Assign Variable
obj_vanish.tx = tx;
obj_vanish.ty = ty;
obj_vanish.v_speed = v_speed;
obj_vanish.txs = txs;
obj_vanish.tys = tys;
obj_vanish.xstep = xstep;
obj_vanish.ystep = ystep;
obj_vanish.v_rot = v_rot;

// Play Audio
audio_play_sound(s_exit, 0, 0);

// Destroy Instance
with(v_obj) instance_destroy();

// Set Global Variable
global.vanishing = true;
}
}
 
Thanks @Tyg.

For applying the motion blur to a small handful of objects, parenting would be the ideal approach. However, I don't think this would be the appropriate approach if the goal was to apply to the whole screen since I think every object would then have to be parented to that one motion blur shader object, which could cause several issues down the road.

What I'm seeing right now is that the motion blur works perfectly when its applied to the sprite created from surface (mainly because its a single sprite). But when the effect is applied to the application_surface, I get wierd results.

This is what is happening in the example, no motion blur effects. Just imagine the red circles are moving left and right randomly:

1606968832061.png


Here is what I get when using the "draw_surface(application_surface, 0, 0)" method (surfaces are messed up, red circles not appearing for some reason):

1606968142674.png


Here is when the shader is applied to the sprite created using the sprite_created_from_surface. This is what I intend:

1606968607472.png

The only problem with the sprite_created_from_surface is that I can't think of a way to make it work on an on-going basis. The sprite created from surface is only a static image and if I try to use an alarm to delete the surface created sprite, the effect resets.

Is it possible to get the result for the surface that appears correctly? For more context, in the example you've provided, it appears that the motion blur is only applied to the two robots in the center but not on the entire screen. How would you apply the motion blur to the whole screen, instead of only applying the motion blur to the background object in addition to the two robots?

Thanks!
 

Tyg

Member
Well if you make a global var and only activate the sprite_create surface while moving...now it only has to be captured once...what are you doing with the background..is it static..is it parallaxing or scrolling ? are you going to have HUd items
 
Last edited:

Tyg

Member
Ok here is a screenshot from a game demoScreenshot (2).jpg
Now i would not want the the motion blur to affect HUD items so would only apply the motion blur everything else but only when im running...is that what you are trying to do?
so that is why im asking what your background is going to do
 
I might have been misunderstanding what you meant by background. I thought you meant background as in the background sprite/object as a separate entity only, not including instances that are doing things in the game. I now see that when you say background, you mean everything that is happening in the game, but under the HUD (hope I'm right!).

The background in this context is not static. Parallax and scrolling are both happening simultaneously (i.e. parallax scrolling). HUD should not be affected. In your game, I assume when the player is running, everything except the hud has a motion blur applied and that this is done as a screen effect below the HUD which remains unaffected, instead of applying the motion blur to each object that is parented to a motion blur object. In your game, I assume you haven't parented obj_player, obj_mech, obj_road, obj_sky etc. to a parent motion blur object.

Thanks!
 
Last edited:

Tyg

Member
if you static just put this on your ogm objects draw event

if global.Moving
{
shader_set(shd_mb);
draw_self();
shader_reset();
}else
draw_self();

there is no need to do any screen capture thats only like for a pause or switch to another room kinda stuff
im trying to see if it can be applied in a post draw event after everythings drawn then apply to surface
the reason i am asking is that if your background is static it can just be a sprite on the background layer and not an object
ok according to your code if you make your room size 1536 x 864 and are scaling it down by 4 you get 384 x 216 but that is not the size of your obm sprite you need to change it to match your scale size
 
Last edited:
No, the background is not intended to be static while the motion blur is in effect. I think a post draw event in the manner you describe might be the appropriate approach. Just a thought - in your own project, in case you don't have it already, if the player can destroy that big robot, then a motion blur screen effect would surely look good when the robot blew up or something.
 
Last edited:

Tyg

Member
yes..the dummy object just has a draw event with the shader...thats it nothing else...so parented objects just inherirt the draw event that only triggers when the global.Moving is set otherwise it defaults to a normal draw..this info would have been helpful many hours ago..but i like
puzzles..np you need to set your room size and and correct the sprite size of obm to 384x216 to fill the screen
i found this works in the dummy obj draw event
-------------------------------------
if global.Moving
{
shader_set(shd_mb); //motion blur shader
draw_surface_ext(application_surface, 0, 0,1.4,1.2,0,c_white,1);
shader_reset();
}else draw_self();
-----------------------
sorry correction draw _self added and a dummy sprite (invisible) was added to the dummy_object
it now blurs only when i hit the movement key(left)
i just have to deal with thoose annoying red balls now...lol
i think it it depth layers that are messing up....you dont really need thoose...just correct the instance creation order in the room editor
 
Last edited:

Tyg

Member
Screenshot (4).jpgScreenshot (12).jpg
Ok first one normal and second one when key pressed left
i think thats what you want but with red balls right?
the background obj can be parallaxed and effect will remain constant but only while moving

Ok i got your ball to work...create another instance layer..i called parralax and put your your obg object which i hope is parented to dummy object ..just below the instance layer
there is just a blend issue with the balls now(fixing)
which is normal when drawing sprites with shaders...its all good

its almost there..working fine
 
Last edited:
Thanks @Tyg, really appreciate all your effort and input with this thing! :)

Perhaps I was not clear before, but the parented object approach is something that should ideally be avoided. In my first post of this thread, I said:

Am able to get motion blur for individual objects, but don't know how to apply it to the whole screen.
which was basically echoing what OP said:

Hey, guys. I was wondering if anybody has some motion blur scripts or shaders to blur everything visible. I can script code to motion blur individual objects and it looks great, but I'd love to see it all blur.
I could have a small handful of objects and apply the motion blur to them via a parent, no issues. But there are countless highly complex objects that are parented to another object, which in turn is a child to another grandparent object. I don't think it'd be wise to have a motion blur parent object added in this chain of inheritance since one would have to then keep track of this at all times and doing so for many objects is not feasible I think (at least until GMS allows multiple parents without grandparenting). This is why I was looking to apply the effect to the whole screen to capture literally every instance in the game that is currently being rendered on the screen, but do so below the HUD (which would be drawn via Draw GUI).

I might be mistaken, but it seems your approach involves emulating a full screen effect by individually applying the motion blur to objects (through parenting), rather than applying a simple "overlay" that captures everything. If not, surely there wouldn't be a need to do any parenting, change sprite sizes etc? The sprite_create_from_surface approach managed to achieve the effect for the full screen without needing any sprite changes, parents etc. (based on the third image in post #14), its only problem was that it could only work statically.
 
Last edited:

Tyg

Member
Ok i figured it out and actually its pretty easy...What you want is to render a layer with the shader only when moving.
This is how to do it
make a global var global.Moving or whatever you want to call it
make theese 2 functions
----------------------------
function Layer_Blur_Start()
{
if global.Moving
if event_type == ev_draw
if event_number == 0
shader_set(shd_mb);
}
----------------------------
function Layer_Blur_End()
{
shader_reset();
}
---------------------------
Now in the room creation code put this(Room Editor Tab, Properties, Creation Code Button)
-------------------------------
var lay_id = layer_get_id("Instances");
layer_script_begin(lay_id, Layer_Blur_Start);
layer_script_end(lay_id, Layer_Blur_End);
---------------------------------------------

Now everything on that layer will render with the BLUR when the global.Moving variable is set to true and rendered normally when set to false
i just check if a key is up or down and set the var.
You just have to make another layer for GUI items
You can alternately put the creation code on a dummy item in its create event but it would have to be the first instance created

From the manual
*************************************************
In this extended example, we will first show you how a simple script function is structured to set some shader uniform data so that when the given layer is drawn, this function will be run and the shader will work correctly. In the example, it is worth noting how we check which event is being called so that the rest of the function is only run on the specific event that we require it to work on - in this case, only on the main draw event:

/// @function layer_shader_start();
function layer_shader_start()
{
if event_type == ev_draw
{
if event_number == 0
{
colour_to_find = shader_get_uniform(sShaderDemo5, "f_Colour1");
colour_to_set = shader_get_uniform(sShaderDemo5, "f_Colour2");
shader_set(s_ColourChanger);
shader_set_uniform_f(colour_to_find, 1,1,1 );
shader_set_uniform_f(colour_to_set, 1,0,0 );
}
}
}

We would then have a companion function to reset the shader after all the drawing is done:

/// @function layer_shader_end();
function layer_shader_end()
{
if event_type == ev_draw
{
if event_number == 0
{
shader_reset();
}
}
}

Now that we have defined our script functions for setting the shader, we then have to assign them to a specific layer so that the layer knows to call them. This would be done in the room creation code, or in the create event or room start event of some controller object (they do not need to be set every step, but rather once at the start of the room, or when the layer is initially created):

var lay_id = layer_get_id("Instances");
layer_script_begin(lay_id, layer_shader_start);
layer_script_end(lay_id, layer_shader_end);

This final code block assigns the scripts to the layer.
For GUI items to appear correctly
Place them on the GUI layer(full room size)
in the Draw Gui Event put this
draw_self();
if its not showing make a dummy draw event like
do_nothing =0;
run it ...once the gui is linked you can delete the draw event
i know it sounds weird but works..Draw Gui is weird like that
or you could just leave it as a normal draw without a draw event but it doesnt scale right


Sorry i wasnt fully understanding the effect you were trying to do
No parenting or screencapture(which would still be goood for a pause screen)
It was a good learning experience

That should do it...works great
Now if you will have to teach me that dropbox thing..lol :)
I can zip it and send it to you
 
Last edited:
I'm attempting to incorporate the items and will let you know shortly of results, but I think it'd be useful to upload your zip file and post it here so it may be helpful for novices to learn from the actual project file itself.

EDIT; works great! the only one issue I'm having is I'm not able to turn the shader off even after setting global.Move to false. When I press a key to trigger it, it becomes true, and show_message shows the variable as 1 and the shader is active. When I press another key to turn it off, global.Move is false, show_message shows the variable as 0, but the shader is still active. Not sure if this is happening on your end, but could be due to difference in GMS 2.2+ and 2.3. Haven't updated yet, but can update and test on a secondary notebook laptop if you can upload the file.

You can upload it on Dropbox.com or Box.com - both are free for uploading around 2 GB worth of data. You'll have to create an account though. If using Dropbox, pick the upload file option, then share the link and paste here.

Here is Dropbox's interface:

1607020787062.png

After clicking "Share":

1607020826890.png

Thanks again!
 
Last edited:
Updated GMS to 2.3+, tried your example and works as expected. I replaced the key check events with a single step event containing the following:

GML:
if keyboard_check(vk_left)
    {global.Moving = true}
    else
    {global.Moving = false}
and it works. Not sure why it didn't work on GMS 2.2+ but more than likely it would have been something due to the version change.

Also, this may be of further importance in case you don't know already. Ideally, you'd want to have the sprites in their own separate texture groups instead of being in the Default group. At the same time, when this effect is in action, you might notice some "cutoff" going on for certain sprites. This can be corrected by unchecking "Automatically Crop" under "Group Settings". In this case though, for some reason, if crop option is checked, and the sprite is in its own texture group, the effect is applied only very mildly. If it is in the default group, and crop option is checked, then you get the cutoff. Not sure why this happens, but texture groups appear to be playing an important part here.

For further discussion on the cutoff issue, Ctrl+F for the posts by M. Atlas (specifically the 2nd post and follow-up) at Xor's website: https://xorshaders.weebly.com/tutorials/blur-shaders-5-part-2

Just like to say thanks again @Tyg for all your efforts. This was a dead thread before, but now contains a ton of useful info thanks to your contributions!
 

Tyg

Member
NP..glad i could help
Ya i just checked the box for separate texture page for the ball sprite and the red ball looks better
That happens with shaders they somtimes bleed if texture page is not set separate
I updated the link with the change
 
Last edited:
Top