SOLVED Drawing a Shader Meant for the Whole Screen on a Smaller Surface

I'm 4 days into making my game and I want a glitch effect for when enemies are damaged, I found bktGlitch which is a really, really good shader from what I can tell. It is meant for use on the application_surface. However, I'd love to use it on my o_simpleEnemy. So I figured out how to make my own surface, called o_surface, and I attached it to my o_simpleEnemy. I'm having trouble actually drawing the shader onto the surface though, no matter what I do it always draws onto the application_surface even when I do not input it into draw_surface. I know this is a very specific issue, but I really don't know what else to do, as I have been performing trial and error on this issue and watching videos about surfaces for the entire day and I need some ideas because I'm out. So I would really appreciate it : )

My code for the create event of o_surface is just:

Code:
surface = surface_create(16, 16);
Here is my code for the draw event of o_surface. This code will cause the shader to apply to the entire screen until the hitflash animation is over, despite the surface clearly being set to "surface" instead of the application_surface. When bktglitch_deactivate(); is enabled, the code does nothing visually. I'm wondering if maybe, despite the "surface" size being set to 16 x 16, if it is stretching that 16 x 16 specifically across the entire application_surface. But to check where "surface" is I used draw_clear_alpha(c_white, 1); and it fit exactly in the place I wanted it to, on top of o_simpleEnemy. I also put the same draw_clear_alpha(c_white, 1); in the top part instead of the else area to see if it was distorting for just the top and got the same result.

Code:
if (surface_exists(surface))
{
    if(o_simpleEnemy.flash > 0)
    {
        surface_set_target(surface);
        bktglitch_activate();
        bktglitch_config_preset(BktGlitchPreset.B);
        draw_surface(surface, o_simpleEnemy.x-8, o_simpleEnemy.y-8);
        //bktglitch_deactivate();    //same as shader_reset();
        surface_reset_target();
    }
    draw_surface(surface, o_simpleEnemy.x-8, o_simpleEnemy.y-8);
}
else
{
    surface = surface_create(16, 16);
    surface_set_target(surface);
    //draw_clear_alpha(c_white, 1);
    surface_reset_target();
}
As I said earlier, I know this is a super specific issue, but any help or advice would be appreciated.
 
Last edited:
The target doesn't change what gets affect, just where stuff is drawn. If you don't reset the shader, everything drawn after will be affected. Try drawing the surface without changing the target to itself.
 
The target doesn't change what gets affect, just where stuff is drawn. If you don't reset the shader, everything drawn after will be affected. Try drawing the surface without changing the target to itself.
The surface's size is 16 x 16, should that not prevent the shader from being applied outside of that area?

Also, if this is what you meant, it has the issue of not applying the shader effect visually. If bktglitch deactivate is enabled, it draws the entire screen. So it effectively makes no difference.

GML:
if (surface_exists(surface))
{
    if(o_simpleEnemy.flash > 0)
    {
        //surface_set_target(surface);
        bktglitch_activate();
        bktglitch_config_preset(BktGlitchPreset.B);
        draw_surface(surface, o_simpleEnemy.x-8, o_simpleEnemy.y-8);
        //bktglitch_deactivate();    //same as shader_reset();
        //surface_reset_target();
    }
    draw_surface(surface, o_simpleEnemy.x-8, o_simpleEnemy.y-8);
}
else
{
    surface = surface_create(16, 16);
    surface_set_target(surface);
    //draw_clear_alpha(c_white, 1);
    surface_reset_target();
}
 
Yes, they wouldn't affect things outside of that range, but that isn't the issue. Shaders work exactly like the state-setting functions like draw_set_alpha() and such. Changing the target only changes where you draw stuff; it doesn't cancel out shaders and whatnot when you reset the target, so if you don't turn off the shader, anything else drawn after this surface will be affected. Depending on how the shader works, you might need to set the surface target, but you'd want to draw the contents of the surface, not the surface itself. I'm not too familiar with the inner workings of GMS2, so I don't know if the current version would not have issues with drawing a surface to itself.

Also, your second example draws the surface twice. Just draw it once with the shader applied.
 
I managed to fix the issue of the shader either applying all the time on the whole screen or not applying at all by moving the code on o_surface to the enemy I want to glitch when shot. HOWEVER this only prevented it from being applied at all times, now it is applied when the enemy is actually shot but still applied to the whole screen. So the only question now is; how can I limit the shader to just the 16x16 area of the o_simpleEnemy?

Here is the updated code. Turning on bktglitch_deactivate will still prevent the shader from ever triggering, ever. The other shader is just a simple invert color shader, made to change a single object unlike the glitch shader.
GML:
draw_self();
if(flash > 0)
{
flash--;
shader_set(sh_invertColor);
draw_self();
shader_reset();
}
if (surface_exists(o_surface.surface))
{
    if(o_simpleEnemy.flash > 0)
    {
        surface_set_target(o_surface.surface);
        draw_sprite(s_simpleEnemy, -1, x, y)
        bktglitch_activate(16, 16);
        bktglitch_config_preset(BktGlitchPreset.A);
        draw_surface(o_surface.surface, x-8, y-8);
        //bktglitch_deactivate();    //same as shader_reset();
        surface_reset_target();
    }
    //draw_surface(surface, o_simpleEnemy.x-8, o_simpleEnemy.y-8);
}
else
{
    o_surface.surface = surface_create(16, 16);
    surface_set_target(o_surface.surface);
    //draw_clear_alpha(c_white, 1);
    surface_reset_target();
}
Pls help
 
If you're going to start a shader, you have to end it when you're done with it, or as I stated, anything afterwards will be affected. If the shader not being reset is seen as a step in the right direction, then you're doing something wrong elsewhere. You're not drawing the surface, and you're making the mistake if drawing it to itself again. Set the target, draw your stuff, reset the target, draw the surface. Drawing to a surface is not the same as drawing the surface itself.
 
You're not drawing the surface, and you're making the mistake if drawing it to itself again.
Oops, you're right.
If you're going to start a shader, you have to end it when you're done with it, or as I stated, anything afterwards will be affected.
Nothing afterwards is being affected, with the code up there, here is what is happening.
Although I do share your concerns as to why that is, if I deactivate the shader by removing those comment /'s then the shader doesn't show up or work at all. Moving it outside the if statement changes nothing.
You're not drawing the surface, and you're making the mistake if drawing it to itself again.
Am I not drawing it because I am drawing it to itself?

Thank you for your patience thus far, I understand it can be difficult to explain this to a newbie.
 
Last edited:
It's not so much that you're drawing it to itself (avoiding the concept, that is the problem in this case), it's that you're not drawing it at all. Think of it like writing a reply to me in this thread: if you don't submit it, it won't show up for me to read it. Once you're done drawing to the surface and you reset the target, that's then when you draw the updated surface.

There's one thing I overlooked, and it's the way you draw to your surface. The reason why nothing is showing is because you're not actually drawing within its bounds. The surface is 16x16, but instead of drawing the sprite at 0,0 in order to put it in the surface, you're still using x and y, so the sprite is drawn way outside of the surface. Only use x and y for where you draw the surface, but when you're drawing to it, just use 0,0.

Edit - and you're drawing the sprite before setting the shader.
 
It's not so much that you're drawing it to itself (avoiding the concept, that is the problem in this case), it's that you're not drawing it at all. Think of it like writing a reply to me in this thread: if you don't submit it, it won't show up for me to read it. Once you're done drawing to the surface and you reset the target, that's then when you draw the updated surface.

There's one thing I overlooked, and it's the way you draw to your surface. The reason why nothing is showing is because you're not actually drawing within its bounds. The surface is 16x16, but instead of drawing the sprite at 0,0 in order to put it in the surface, you're still using x and y, so the sprite is drawn way outside of the surface. Only use x and y for where you draw the surface, but when you're drawing to it, just use 0,0.

Edit - and you're drawing the sprite before setting the shader.
I did everything you said and it worked! Thank you, you are very kind, but now I've run into a new issue. I feel bad that this isn't working as intended yet, I don't want to tire you, so please if you're tired of helping don't push it. I suspect however, that this is the last problem before it works. The problem being: For some reason, the surface is an 8x8 area, despite me setting it to 16x16. So first I changed all the places where I set surface size to double, which did nothing, still a 8x8 area. Then I squared the area to make 128x128 and still no change. I remembered that my camera has some halving equations and even though those don't touch the surface, I tried disabling those and no change as expected. So I finally used CTRL+Shift+F and searched for every time the word "surface" is used and the only time it is used is to set it's size when the game starts and if for some reason it disappears, these are expected assignments to the surface. Nothing else ever touches the size.

Here is what that looks like.

And here is the code you helped me with, thanks! I think I finally got it right. I cut out the previous if statement since it was redundant.

GML:
draw_self();
if (surface_exists(o_surface.surface))
{
    if(flash > 0)
    {
        flash--;
        surface_set_target(o_surface.surface);
        bktglitch_activate();
        bktglitch_config_preset(BktGlitchPreset.A);
        draw_sprite(s_simpleEnemy, 0, 0, 0);
        bktglitch_deactivate();//same as shader_reset();
        //shader_set(sh_invertColor);
        //draw_sprite(s_simpleEnemy, 0, 0, 0)
        //shader_reset();
        surface_reset_target();
        draw_surface(o_surface.surface, x-4, y-4);
    }
}
else
{
    o_surface.surface = surface_create(16, 16);
    surface_set_target(o_surface.surface);
    //draw_clear_alpha(c_white, 1);
    surface_reset_target();
}
You have been very helpful, I cannot thank you enough.

EDIT: Also I tried doubling the size of the sprite, but that did not increase the surface of course.
 
Last edited:
I figured it out, thanks!

GML:
draw_self();

if (surface_exists(o_surface.surface))
{
    if(flash > 0)
    {
        flash--;
        draw_sprite(s_simpleEnemyInvert, 0, 24, 24);
        surface_set_target(o_surface.surface);
        bktglitch_activate(24, 24);
        bktglitch_config_preset(BktGlitchPreset.F);
        draw_sprite(s_simpleEnemyInvert, 0, 24, 24);
        bktglitch_deactivate();//same as shader_reset();
        //shader_set(sh_invertColor);
        //draw_sprite(s_simpleEnemy, 0, 0, 0);
        //shader_reset();
        surface_reset_target();
        draw_surface(o_surface.surface, x-24, y-24);
    }
}
else
{
    o_surface.surface = surface_create(128, 128);
    surface_set_target(o_surface.surface);
    //draw_clear_alpha(c_white, 1);
    surface_reset_target();
}
/*if(flash2 > 0) and (flash == 0)
    {
        flash2--;
        draw_sprite(s_simpleEnemyInvert, 0, 0, 0);
    }
What was happening is I would increase the surface size without moving the sprite to the center again, so it would draw it small at the numbers before, and draw it at the edge with with only a portion of the sprite showing when I increased the surface size.
 
Last edited:
Top