Asset - Shaders Retro Palette Swapper

First I would like to thank Pixelated Pope for making this shader. It works really well, and I wouldnt be able to work on my current game project without it.

There is just one thing that I really like to be able to do, and that is to use colors with alpha values, like 20% transparent, but it looks like it is not the background color that can be seen through the transparent color, but the original color of the sprite that I use the palette_swapper with - I of course would like that it is the background graphics that can be seen through the transparent color.

The reason I try to make this work it because now that game maker´s "colour interpolation" does not work with palette_swapper, I attempt to draw my own smooth curves manually.

edit : perhaps I can add the shader "Better Scaling" after using palette_swapper and get the antialias I want that way somehow.
 
Last edited:
Yeah, unfortunately, this is likely never going to happen. It's right there in the name: "Retro" Palette Swapper. If you are using smooth curves and anti-aliased sprites with semi transparent edges and hundreds of unique colors... you'll need to find another solution to your problem.
 
This is a really cool asset. Thank you. I am about to ask a question that's likely pretty dumb, but the truth is that I'm not fully sure how this works. I went through the examples provided via the demo and got an idea. I read your initial how-to guide on this forum post, as well.

If I did something as simple as just doing the pal_swap_set on a Step event, it worked beautifully.

However, if I try to then do pal_swap_set on something else at the same time, it undoes the palette swap on the sprite that was swapped first.

Fair enough, I was most likely too hasty to just assume it's really that simple. Looking through your examples, I see you did the actual palette swap process itself via Draw GUI. I copied your code, changing only a couple things to suit, so I could take a better look at the result:

GML:
pal_swap_set(s_player_pal,122,false);
    draw_sprite_ext(sprite_index,image_index,GUI_W * .45,GUI_H*.55,image_xscale,image_yscale,image_angle,image_blend,image_alpha);
pal_swap_reset();
What happens is that it draws a palette-swapped sprite on the screen but the object I'm looking to change is still the same. In fact, the object with the original color and the re-colored sprite exist at the same time.

Obviously at this point, I'm making some dumb mistakes, so I come to you to and try and better understand what I should be doing to make this work. I've done all of the above within the o_player object. If I wanted to just change the palette within the o_player object, how would I go about doing that?

In obj_basic_example provided by your demo, I see stuff in the Create section regarding draw_palette and draw_code. Should I be doing anything with these? For clarification, the ONLY thing I had in my Draw GUI (and when I tried with the Step event) is the small code block shown above. Nothing else. I honestly wasn't sure what bits and pieces were for sake of the demo and which were necessary things to have in actual use.
 
@Crysillion
" If I did something as simple as just doing the pal_swap_set on a Step event, it worked beautifully. " This line has me worried. None of this should be done in the step event. It should all be done in the draw event. If you are really in the step event, that is 100% of your issue :D
 
Yeah, so as I said, I moved over to putting it onto Draw GUI and tried to follow more closely what's in the example. The issue is that it draws a sprite in the center of the screen, but that sprite is not tied to the o_player object the way I want, so I end up having two sprites on screen. One that I can't control and is palette swapped, and the other that I can control that isn't palette swapped.

Here's an example of what I mean: unknown.png

The white ship is the o_player object. The white and red colors are default. But, there's also a black and blue ship, which is that way because it's palette swapped. It rotates to turn with the player, but isn't controlled elsewise. I'm trying to figure out why this is.

My Draw GUI code:

Code:
pal_swap_set(s_player_pal,122,false);
    draw_sprite_ext(sprite_index,image_index,GUI_W * .5,GUI_H*.5,3,3,image_angle,image_blend,image_alpha);
pal_swap_reset();
It makes sense to me that this ship shows up as its own sprite because that's literally what this code is doing. My confusion lay in trying to apply this palette swap to the o_player object instead of it being its own sprite like this.

I was only able to attach it to the o_player object by placing it into the Step event which, as you've pointed out, is very much so the wrong way to do things.

What would be the right way to do things?
 
Last edited:
Why are you using the draw GUI event. You should be doing this in the DRAW event. And between the pal_swap_set() and pal_swap_reset() just put draw_self.

Your draw event should look like this:

Code:
pal_swap_set(s_player_pal,122,false);
draw_self();
pal_swap_reset();
For further assistance, I really recommend joining the discord server by following the link in my signature. You can get help there 24/7 for anything GML related and the process will be much much much faster and not rely on me being available to help you.
 

jjjjj

Member
Hi @Pixelated_Pope! Thank you for making this very excellent and useful tool.

I know this thread is old now, but shooting my shot that I can get some help in having palette swapping play nice with an outlining shader I implemented from a tutorial.

Can you let me know if anything pops out at you that may be causing a conflict here? I really appreciate you taking a look.

Actor's draw event:
GML:
shader_set(shader_Outline);

var texture = sprite_get_texture(spriteIndex, image_index);
var textureWidth = texture_get_texel_width(texture);
var textureHeight = texture_get_texel_height(texture);
shader_set_uniform_f(outlineTexelHandle, textureWidth, textureHeight);

var colorTintHex = color_to_hex(colorTint);
var colorTintRGB = hex_to_rgb(colorTintHex);
var colorTintAlpha = 0.8;

shader_set_uniform_f(outlineColorHandle, normalize_rgb(colorTintRGB[0]), normalize_rgb(colorTintRGB[1]), normalize_rgb(colorTintRGB[2]), colorTintAlpha); //passes values to shader's outlineColorRGBA vec4

pal_swap_set(sp_Hyper_Palette, global.paletteSwapCur, false); //wired to a global variable so I can test swapping on a key press
draw_sprite_ext(spriteIndex, image_index, selfDrawX, selfDrawY, scale * facing, scale * righting, rotation, c_white, 1);
pal_swap_reset();

shader_reset();
Outline shader FSH:
Code:
varying vec2 v_vTexcoord;
varying vec4 v_vColour;

uniform vec2 outlineTexelWidthHeight;
uniform vec4 outlineColorRGBA;

void main()
{
    vec4 outlineColor = v_vColour * texture2D(gm_BaseTexture, v_vTexcoord);
  
    vec2 outlineWidth = outlineTexelWidthHeight * 1.0;
  
    vec4 outlineRGBA = outlineColorRGBA;
  
    if (texture2D(gm_BaseTexture, v_vTexcoord).a <= 0.0) {
        float alpha = 0.0;
      
        alpha = max(alpha, texture2D(gm_BaseTexture, vec2(v_vTexcoord.x - outlineWidth.x, v_vTexcoord.y)).a);
        alpha += max(alpha, texture2D(gm_BaseTexture, vec2(v_vTexcoord.x + outlineWidth.x, v_vTexcoord.y)).a);
        alpha += max(alpha, texture2D(gm_BaseTexture, vec2(v_vTexcoord.x, v_vTexcoord.y - outlineWidth.y)).a);
        alpha += max(alpha, texture2D(gm_BaseTexture, vec2(v_vTexcoord.x, v_vTexcoord.y + outlineWidth.y)).a);
              
        if (alpha > 0.0) {
            outlineColor = outlineRGBA;
        }
    }
  
    gl_FragColor = outlineColor;
}
The palette swapping works always, but the outline shader works only when the palette is not swapped.


capture_23-10-06-0608.png
Original palette - the dynamic blue outline draws as expected.


capture_23-10-06-0623.png
An alternate palette - the dynamic blue outline is absent.
 
Yeah, you can't do that. You need to draw your sprite, with the outline, to a new surface. Then draw that surface pal swapped to the screen. This is just a general shader thing. You have two main options when "combining" shader effects.
1. Use a surface to store the "result" of each stage of the shader and then draw that surface after everything is done.
2. Create a super shader that combines the functionality of all desired shaders.

Most people go with 1. Especially if they didn't write all the desired shader effects.
And, when you draw your outline surface, make sure to make the surface a bit larger than your sprite to leave room for the outline.
 

jjjjj

Member
Yeah, you can't do that. You need to draw your sprite, with the outline, to a new surface. Then draw that surface pal swapped to the screen. This is just a general shader thing. You have two main options when "combining" shader effects.
1. Use a surface to store the "result" of each stage of the shader and then draw that surface after everything is done.
2. Create a super shader that combines the functionality of all desired shaders.

Most people go with 1. Especially if they didn't write all the desired shader effects.
And, when you draw your outline surface, make sure to make the surface a bit larger than your sprite to leave room for the outline.
This is exactly the direction I needed, thank you!
 
Top