Canvas Group Outline
Adds an outer stroke to a Canvas Group. It works well with pixel art and assets with anti-aliased edges and accounts for the modulate
colour.
This shader creates an outline by applying dilation to the Canvas Group’s alpha channel (using a square kernel) and blending colours.
I adapted this shader from other solutions that couldn’t suit my needs. This is also my first shader, any feedback is appreciated!
Shader code
shader_type canvas_item;
uniform vec3 line_colour: source_color = vec3(1.0);
uniform int line_thickness: hint_range(0, 10) = 1;
uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;
void fragment() {
vec2 size = SCREEN_PIXEL_SIZE * float(line_thickness);
float line_alpha = 0.0;
for (float i = -size.x; i <= size.x; i += SCREEN_PIXEL_SIZE.x) {
for (float j = -size.y; j <= size.y; j += SCREEN_PIXEL_SIZE.y) {
line_alpha += texture(screen_texture, SCREEN_UV + vec2(i, j)).a;
}
}
vec4 colour = texture(screen_texture, SCREEN_UV);
vec4 outline = vec4(line_colour, min(line_alpha, 1.0));
COLOR *= mix(outline, colour, colour.a);
}
If anyone’s having trouble with lights or CanvasModulate being double-applied to things with this shader on, adding ‘render_mode unshaded’ at the top should fix it. Though do note that this means the lights are applied to the canvas group but not the outline, which is right for my use case but may not be right for yours.
cool
Hello there! Thank you very much for this Shader. Sadly, I can’t use it because it won’t work with z-Indexed Nodes, meaning Sprite2Ds for example, which have a z-Index other than “0”. My game demands the use of z-Indexes. Could you provide any solution to this? 🙂
Hello, sorry for my late reply! I had a similar issue in my project. Unfortunately, due to draw order, changing the Z-index of children makes them practically invisible to the CanvasGroup’s shader. However, I worked around this in two ways:
Let me know if this helped you!
I see a bug where if the node is zoomed out or in in the editor or in game, the outlines gets bigger or smaller (respectively). Not sure if it’s your code or something about my setup.
Hello! Not a bug: fragment coordinates are always window-relative, not world-relative. Therefore, the line thickness seems to grow or shrink when compared to the 2D world, but its size is actually constant with respect to the viewport.