• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Shaders [SOLVED] Shaders: Post Processing, Multiple passes?

c023-DeV

Member
How do you handle multiple post processing shader passes?

Can I do multiple passes of the application surface within one shader?

Or how can I pass a surface that has be processed by one shader to another shader?

Thanks!
 

Simon Gust

Member
Try this
Code:
// pass 1
surface_set_target(SURFACE B);
// draw stuff
surface_reset_target();

// pass 2
shader_set(SHADER);
surface_set_target(SURFACE A);
repeat (6) // repeated 6 times
{    
    draw_surface(SURFACE B, 0, 0);
    surface_copy(SURFACE B, 0, 0, SURFACE A);
}
surface_reset_target();
shader_reset();

// result will be on SURFACE A
 

c023-DeV

Member
[edit]
Awww, Thanks @Simon Gust .

The second surface did the trick.

Code:
surface_set_target(surf_buffer2);
        shader_set(sh_pixel);
        var vw = camera_get_view_width(view_camera[0])/2;
        var vh = camera_get_view_height(view_camera[0])/2;
        shader_set_uniform_f(pixel,vw,vh,size,size);
        draw_surface_ext(application_surface,0,0,1,1,0,c_white,1);

        shader_reset();
        surface_reset_target();
        
        surface_set_target(surf_buffer);
        shader_set(shd_chromatic);
        var tmpDisVis = round((distort/2*400)/1000);
        shader_set_uniform_f(cAStr, tmpDisVis);
        draw_surface_ext(surf_buffer2,0,0,1,1,0,c_white,1);
        shader_reset();
        surface_reset_target();
        
         shader_set(shaderLight);
         draw_surface_ext(surf_buffer,0,0,1,1,0,c_white,1);
         shader_reset();
works, and is this the way to "stack" shaders? or is it better to have them processed in one?
 
Last edited:

Simon Gust

Member
I've tried that.
My problem seems to be that my shaders have a line like:

Code:
texture2D( gm_BaseTexture , v_vTexcoord)
I guess the "gm_BaseTexture" means that the shader will sample what GM has in that sampler 'slot' ?! So I always get the unprocessed application surface in that case.

(I'm pretty new to shaders, I'm trying to combine a pixelation shader with RGB shifting)
Just make sure you put the code in the Draw end event so that everything is drawn to the application surface first.
I'm guessing you modified my code, how exactly does your code look now?
 

kupo15

Member
I think there is an even easier way to do this that is more efficient that creating another shader. Instead of creating another surface simply reference the application surface instead. The application surface IS a surface like the ones you create except its done automatically so it behaves the same as your created surfaces.

Simon might be right about when you should apply your shader effects in a different event...though I think maybe in the post draw event might be the place instead of the Draw End. the Draw end, I believe is still part of the drawing process so if there is any objects with a draw end event its not going to get processed by the shader. The Post Draw events occur AFTER the application surface has been completely drawn

So simply this (I think):

Post Draw Event:
shader_set()
draw_surface(application_surface)
shader_reset()

repeat with how many shader effects you want to use

NOTE: I am not 100% sure about this, would like confirmation from someone else who has more experience with this type of post processing effects
 

Simon Gust

Member
I think there is an even easier way to do this that is more efficient that creating another shader. Instead of creating another surface simply reference the application surface instead. The application surface IS a surface like the ones you create except its done automatically so it behaves the same as your created surfaces.

Simon might be right about when you should apply your shader effects in a different event...though I think maybe in the post draw event might be the place instead of the Draw End. the Draw end, I believe is still part of the drawing process so if there is any objects with a draw end event its not going to get processed by the shader. The Post Draw events occur AFTER the application surface has been completely drawn

So simply this (I think):

Post Draw Event:
shader_set()
draw_surface(application_surface)
shader_reset()

repeat with how many shader effects you want to use

NOTE: I am not 100% sure about this, would like confirmation from someone else who has more experience with this type of post processing effects
Yes, post draw is the correct sub event, but just in case you wanted for something to be unaffected by the shader, you'd have to call it earlier.

with the temporary surface creating / copying I was thinking of shaders that need to run multiple times for the effect to take place.
 

kupo15

Member
Yes, post draw is the correct sub event, but just in case you wanted for something to be unaffected by the shader, you'd have to call it earlier.

with the temporary surface creating / copying I was thinking of shaders that need to run multiple times for the effect to take place.
Ah that's true, is the Draw Gui a different surface or does that get drawn last to the application surface? If he didn't want that to run through the shader he could still put it in the post draw event and simply make his own gui drawing after that. But like you said, he would have to do your method if certain elements of the game shouldn't go through that shader
 

Simon Gust

Member
Ah that's true, is the Draw Gui a different surface or does that get drawn last to the application surface? If he didn't want that to run through the shader he could still put it in the post draw event and simply make his own gui drawing after that. But like you said, he would have to do your method if certain elements of the game shouldn't go through that shader
The GUI has no surface I believe, as the application surface is sent to the backbuffer first before the GUI subevent even begins. I think it just puts everything directly into the backbuffer.
https://docs.yoyogames.com/source/dadiospice/002_reference/surfaces/the application surface.html
 

c023-DeV

Member
This works:

I have :
Code:
///Create Event
application_surface_draw_enable(false);
In the regular draw event I prepare and compose surfaces...
And then I'm doing the final renders in the Draw GUI event of my obj_GlobalDisplay like this:
Code:
///Draw GUI
scr_render_Final();
if (!showHUD || !instance_exists(obj_pl_ror)) exit;
scr_render_IngameHuD();
scr_render_debugHUD();
the scr_renderFinal() does the shader passes (in the drawGUI) like this:
Code:
///scr_renderFinal()
        surface_set_target(surf_buffer2);
        shader_set(sh_pixel);
        draw_surface_ext(application_surface,0,0,1,1,0,c_white,1);
        shader_reset();
        surface_reset_target();
     
        surface_set_target(surf_buffer);
        shader_set(shd_chromatic);
        draw_surface_ext(surf_buffer2,0,0,1,1,0,c_white,1);
        shader_reset();
        surface_reset_target();
       
        shader_set(shaderLight);
        draw_surface_ext(surf_buffer,0,0,1,1,0,c_white,1);
        shader_reset();
I still would like to learn more about shaders. Any recommended further reading material on GLSL ES and Gamemaker? (Aside from yoyo tech blog)
 
Last edited:

kupo15

Member
The GUI has no surface I believe, as the application surface is sent to the backbuffer first before the GUI subevent even begins. I think it just puts everything directly into the backbuffer.
https://docs.yoyogames.com/source/dadiospice/002_reference/surfaces/the application surface.html
Interesting, you might be right then. And I learned a bit more about rendering...its actually the backbuffer that displays the image we see and not the surface itself. I guess the surface could be compared to a stamp. You create the stamp design you want then transfer it onto the paper (back buffer) which is then displayed to the player.

Knowing this (and if my explanation is correct), is there has to be times where its smarter to simply draw straight to the back buffer without going through a surface. I would imagine going directly to the back buffer is more efficient because you are only drawing things once instead of twice
 

Simon Gust

Member
Interesting, you might be right then. And I learned a bit more about rendering...its actually the backbuffer that displays the image we see and not the surface itself. I guess the surface could be compared to a stamp. You create the stamp design you want then transfer it onto the paper (back buffer) which is then displayed to the player.

Knowing this (and if my explanation is correct), is there has to be times where its smarter to simply draw straight to the back buffer without going through a surface. I would imagine going directly to the back buffer is more efficient because you are only drawing things once instead of twice
Well, you're only drawing 1 item less and you lose out on sub-pixel-less scaling, having a shader affect the application surface, copying what's currently on screen (screenshots) and some more stuff.
The manual says that it may be beneficial on older devices. https://docs.yoyogames.com/source/dadiospice/002_reference/surfaces/application_surface_enable.html
 

kupo15

Member
Well, you're only drawing 1 item less and you lose out on sub-pixel-less scaling, having a shader affect the application surface, copying what's currently on screen (screenshots) and some more stuff.
The manual says that it may be beneficial on older devices. https://docs.yoyogames.com/source/dadiospice/002_reference/surfaces/application_surface_enable.html
Interesting, so it may not be as beneficial as it may seem then. Changing resolutions would be much more of a pain to do without using the application surface or surfaces in general. Still an interesting study
 
Top