Legacy GM Outline Shader (SOLVED)

Odolwa

Member
I have just watched a video Shaun Spaulding posted on applying a black outline to in-game sprites, but I've not been able to implement it. Can anyone help? Here's his code:

Fragment Shader:
Code:
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
uniform float pixelH;
uniform float pixelW;

void main()
{
    vec2 offsetx;
    offsetx.x = pixelW;
    vec2 offsety;
    offsety.y = pixelH;
   
    float alpha = texture2D(gm_BaseTexture, v_vTexcoord).a;
   
    alpha = max(alpha, texture2D(gm_BaseTexture, v_vTexcoord + offsetx).a);
    alpha = max(alpha, texture2D(gm_BaseTexture, v_vTexcoord - offsetx).a);
    alpha = max(alpha, texture2D(gm_BaseTexture, v_vTexcoord + offsety).a);
    alpha = max(alpha, texture2D(gm_BaseTexture, v_vTexcoord - offsety).a);

    gl_FragColor = v_vColour * texture2D( gm_BaseTexture, v_vTexcoord );
    gl_FragColor.a = alpha;
}
And in the player object I put the following:

Create Event:
Code:
//Get 'uniform's for 'shd_outline'
u_ht = shader_get_uniform(shd_outline, "pixelH"); 
u_width = shader_get_uniform(shd_outline, "pixelW");
Draw Event:
Code:
if instance_exists(obj_player)
{
    shader_set(shd_outline); 
    shader_set_uniform_f(u_ht, sprite_height); 
    shader_set_uniform_f(u_width, sprite_width);
    shader_reset();
}
 

Tthecreator

Your Creator!
You are supposed to put your won drawing code right here:
Code:
if instance_exists(obj_player)
{
   shader_set(shd_outline);
    shader_set_uniform_f(u_ht, sprite_height);
    shader_set_uniform_f(u_width, sprite_width);
////YOU PUT YOUR DRAW CODE HERE, so if you want to draw a sprite, you put that here.
    shader_reset();
}
 

Odolwa

Member
Thanks for responding. I added the following:
Code:
draw_sprite_ext(sprite_index, 0, x, y, facing, 1, 0, -1, 1); //'facing' is my own 'xscale' variable.  I use it everywhere else in the player code.
The player sprite still doesn't have an outline, though...
 

GMWolf

aka fel666
Try this:
Code:
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
uniform float pixelH;
uniform float pixelW;
void main()
{
    vec2 offsetx;
    offsetx.x = vec2(pixelW, 0);
    vec2 offsety;
    offsety.y = vec2(0, pixelH);
  
    float alpha = texture2D(gm_BaseTexture, v_vTexcoord).a;
  
    alpha = max(alpha, texture2D(gm_BaseTexture, v_vTexcoord + offsetx).a);
    alpha = max(alpha, texture2D(gm_BaseTexture, v_vTexcoord - offsetx).a);
    alpha = max(alpha, texture2D(gm_BaseTexture, v_vTexcoord + offsety).a);
    alpha = max(alpha, texture2D(gm_BaseTexture, v_vTexcoord - offsety).a);
    gl_FragColor = v_vColour * texture2D( gm_BaseTexture, v_vTexcoord );
    gl_FragColor.a = alpha;
}
Code:
shader_set(shd_outline);
    shader_set_uniform_f(u_ht, 1/sprite_height);
    shader_set_uniform_f(u_width, 1/sprite_width);
   draw_sprite_ext(sprite_index, 0, x, y, facing, 1, 0, -1, 1);
    shader_reset();
This assumes that the uv range is 0-1, so make sure to tick 'use for 3d' the player sprite.
You could do some extra math using the sprite UV's to avoid using 'use for 3d' (and ideally, you should). But this is a bit more complex :)
 

Odolwa

Member
Thanks, Fel666. Unfortunately, running the code now throws the following error:
Code:
In Shader shd_outline at line 13: 'assign': cannot convert from '2-component vector of float' to 'float'
In Shader shd_outline at line 15: 'assign': cannot convert from '2-component vector of float' to 'float'
 

GMWolf

aka fel666
Thanks, Fel666. Unfortunately, running the code now throws the following error:
Code:
In Shader shd_outline at line 13: 'assign': cannot convert from '2-component vector of float' to 'float'
In Shader shd_outline at line 15: 'assign': cannot convert from '2-component vector of float' to 'float'
oh. hahaha sorry, i derped
try this:
Code:
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
uniform float pixelH;
uniform float pixelW;
void main()
{
    vec2 offsetx = vec2(pixelW, 0);
    vec2 offsety = vec2(0, pixelH);
 
    float alpha = texture2D(gm_BaseTexture, v_vTexcoord).a;
 
    alpha = max(alpha, texture2D(gm_BaseTexture, v_vTexcoord + offsetx).a);
    alpha = max(alpha, texture2D(gm_BaseTexture, v_vTexcoord - offsetx).a);
    alpha = max(alpha, texture2D(gm_BaseTexture, v_vTexcoord + offsety).a);
    alpha = max(alpha, texture2D(gm_BaseTexture, v_vTexcoord - offsety).a);
    gl_FragColor = v_vColour * texture2D( gm_BaseTexture, v_vTexcoord );
    gl_FragColor.a = alpha;
}
Actually, this really doesnt change the shader at all..
But the way you draw the sprite is whats important:
You want to pass in the % size of pixels (1/width and 1/height) and make sure that you tick use for 3d as decribed in my earlier post.
 

JaimitoEs

Member
Thanks for responding. I added the following:
Code:
draw_sprite_ext(sprite_index, 0, x, y, facing, 1, 0, -1, 1); //'facing' is my own 'xscale' variable.  I use it everywhere else in the player code.
The player sprite still doesn't have an outline, though...
How big is your sprite? its pixel art or hand painted? maybe the outline is there but having a big asset you can not apreciate the outlined effect.
 

Odolwa

Member
Fel666: The errors are gone, but it's still not showing any outline.
JaimitoEs: It's a 32x32 pixel image I drew in GameMaker. Basically just a square representing the player.

On a side note, if I comment out 'shader_reset();' in the Draw event, walking past other sprites draws a large black box around them and some other sprites are altered. Just thought I'd mention it.
 

JaimitoEs

Member
Ok mmmm, i´m getting the black outlines on my sprites, without passing the uniforms in the draw event.

So, just set the original shader of @ShaunJS in the draw event, draw your sprite and reset it, without setting the uniforms.

Code:
shader_set(shd_outline);
draw_sprite_ext(sprite_index, 0, x, y, facing, 1, 0, -1, 1);
shader_reset();
 
Last edited:

Odolwa

Member
JaimitoEs, I tried that but still no change. I even made a new project and cut everything out except the shader, a 32x32 white block w/ only the code you posted in the Draw event, a simple blue background and I still can't get it to work. I've no idea what I'm doing wrong, or if I'm missing something else. Would you be willing to post a file of the shader working for you? It's the only clue I have to go on right now.
 

JaimitoEs

Member
Ok, the main problem of this shader is the outline color, this one is drawn with the same color of the outlined sprite, so for example, if the sprite have a red termination color, the outline shader is drawn with a red color. Try yourself drawing 2 times the sprite, 1 applying the shader and the other not, an there you can see the difference between this. Try a sprite with alpha value and diferent colors to cleary view the differences between..

The shader code i´ve tried is exactly the same as your first post.
 
Last edited:

Odolwa

Member
Going on your advice, I created a 2nd instance of the object in the room which does not call the shader, but I still don't see an outline.
 

JaimitoEs

Member
Going on your advice, I created a 2nd instance of the object in the room which does not call the shader, but I still don't see an outline.
This is i was trying to tell you, you cannot look the outline because the outline is drawn with the same color as the sprite, if you put 2 white blocks, 1 with shader and the other not, the only distinction is one of them is 1 pixel bigger than the other in both coordinates (33x33 in this case), but with the same colors, this extra white line pixelation is the suposed outline. Maybe @ShaunJS can tell us how to do in the correct way with this shader and what´s we are missing.

I think this case need to pass a 2d sampler to complete the process. Another easy way is draw 2 times the sprite with diferent scales and colors.......
 
Last edited:
What the shader does is set the alpha to the edges higher. The background color of the transparent pixels should be black for a black outline. If you have a rectangle that is filling up the whole image, there is no edges to use.

Try adding some padding to the rectangle. Completely transparent padding using black as the color. So if your rectangle is 32×32, make the image size like 36×36 with the rectangle centered. Fill the extra space with black, alpha 0.

Something else that may help, the shader could be relying on images using premultiplied alpha? If you use that, it might help as well.
 

Odolwa

Member
Apologies for the lateness of my reply.

Strawbry_Jam: I went back and made space around the sprite, like you suggested, and that has done the trick. Many thanks.

Just to summarize what I've got for the benefit of others:
1. The sprite has empty space around it in the Sprite Properties where it's drawn.
2. The 'Used for 3D (Must be a power of 2)' box is ticked.
3. I'm using the shader & Create event code (i.e. I'm making use of the uniforms) I posted in my OP.
4. The code in the Draw event was adjusted to Fel666's OP (i.e. you need to include the '1 / ' in '1 / sprite_height' & width)' and also includes the 'draw_sprite_ext()' function suggested by Tthecreator.

Thanks to everyone who contributed. I appreciate the help, whatever the outcome.
 
Last edited:
C

cambalinho

Guest
i'm sorry, but it's possible choose a color instead use the backcolor?
 
Top