Add glow to sprite that is behind another sprite

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By WingmanImd
:warning: Old Version Published before Godot 3 was released.

Hello!
I want to replicate the glow/stroke effect from Age of Empires II (see photo bellow).

Basically, I want to stroke the part of the sprite that is behind another sprite.
So if I have a “soldier” on Z = 0 and a “building” on Z = 1 I want to show the contour of the soldier on Z = 3 if that makes sense.

Does anybody know how to do that?
Thanks!

Example: What i want

This is typically done in 3D using a Z-buffer, but in 2D there is no such thing so you have to rely on draw order and separate buildings and units in two drawing passes.
One way is to draw all units in a buffer, then you add a shader on buildings that reads the units buffer. If a building pixel is opaque and the units buffer is also opaque, then you can draw the outline instead of the unit.
If the building pixel is transparent, then you draw the unit.

  1. Draw terrain
  2. Draw units in buffer
  3. Draw this buffer on screen
  4. Draw buildings (which now can access the units buffer in their shader to draw outlines)

This is theory, I don’t know yet how it applies with Godot, probably using viewports and a RenderTexture.

Zylann | 2017-05-24 16:29

Yeah, I was thinking about something similar but I have no idea on how to do it in Godot.
Thanks for confirming my theory!

WingmanImd | 2017-05-24 16:33

How about a Sprite > CanvasItem > Behind Parent on/off and glow on /off too?
or am out?

Bishop | 2017-05-24 17:21

I don’t really understand what you want to do. Can you please provide a demo or something?
Thanks a lot1

WingmanImd | 2017-05-24 17:31

In Sprite node there is Behind Parent so with a script + enable/disable glow
but maybe I’m wrong …something like… Area2D.get_overlapping_bodies(Method)?
I’m novice in GDscript

if Area.overlaps_body() :
Sprite.is_draw_behind_parent_enabled(true)
else:
Sprite.is_draw_behind_parent_enabled(false)
is_draw_behind_parent_enabled()

Bishop | 2017-05-24 18:35

That would work. But If I would to use this on an animated sprite with 8 rotation and 5 animations wouldn’t this require to double every frame? Zylann’s solutions seems very good after some more documentation. Thanks!

WingmanImd | 2017-05-24 18:44

Putting a sprite above the unit is a simple solution too but it has limitations:

  1. You have to detect when to do it obviously
  2. You can’t have it partially hidden, it’s all or nothing

Zylann | 2017-05-24 18:46

You can animate Behind Parent and call this animation?..in Godot is everything animatable…but I guess that’s the same as by script and it is the worst solution :slight_smile:

Bishop | 2017-05-24 18:50

Maybe you can add a light that takes the parent sprite and only affect buildings mask, but that will need some light shader work to get the correct output (like the outline) and a fine lighting mask setup.

This is Cubio with a light on wall tilemap layer:
https://lut.im/xttobLEAv4/Txh4JTu5TQ2hmrh1.png

eons | 2017-05-25 04:11

:bust_in_silhouette: Reply From: mollusca

This probably isn’t the optimal method, but it’s easy to set up at least. The scene should look like this:

root
    mask_sprite1
    mask_sprite2 ...etc
    BackBufferCopy
    background
    building_sprite1
    building_sprite2 ...etc
    unit_sprite1
    unit_sprite2 ...etc

The mask sprites are in the same positions as the corresponding building sprites and have the same textures. The BackBufferCopy is set to copy the viewport. The mask-sprites are drawn with this fragment shader:

COLOR = color(1.0, 0.0, 0.0, tex(TEXTURE, UV).a);

The unit-sprites are drawn with this fragment shader:

uniform float outline_thickness = 2.0;
uniform color outline_color = color(1.0, 1.0, 1.0, 1.0);

color col;

if(texscreen(SCREEN_UV).r < 1.0) {
    col = tex(TEXTURE, UV);
}
else {
    float alpha = 4 * tex(TEXTURE, UV).a;
    alpha -= tex(TEXTURE, UV + vec2(TEXTURE_PIXEL_SIZE.x * outline_thickness, 0.0)).a;
    alpha -= tex(TEXTURE, UV + vec2(-TEXTURE_PIXEL_SIZE.x * outline_thickness, 0.0)).a;
    alpha -= tex(TEXTURE, UV + vec2(0.0, TEXTURE_PIXEL_SIZE.y * outline_thickness)).a;
    alpha -= tex(TEXTURE, UV + vec2(0.0, -TEXTURE_PIXEL_SIZE.y * outline_thickness)).a;
    col = color(outline_color.rgb, alpha);
}

COLOR = col;

If this works I’ll love you forever. :slight_smile:
Seems like it’s time to learn how to make fragment shaders.

Thanks!

WingmanImd | 2017-05-25 19:59

Can’t seem to make it work. :frowning:
Can you provide a project please?

WingmanImd | 2017-05-25 20:17

Here’s a simple demo. You can move the ‘unit’ with the arrow keys and pan the camera by clicking and dragging.

MEGA

mollusca | 2017-05-25 21:16

Thanks a lot! You’re awesome!
I’ve been trying to make it work for an hour now but with no success.

Exactly what I wanted. Time to see how the code works now. :smiley:

WingmanImd | 2017-05-25 21:20