2D Topdown Shadow that goes out of bounds

Shader code
shader_type canvas_item;

uniform float r = 0.0;
uniform float offset: hint_range(0.0, 1.0) = 0.0;

void vertex() {
	VERTEX.xy *= 3.;
}

void fragment() {
	mat2 rotation_matrix = mat2(vec2(cos(r), sin(r)), vec2(-sin(r), cos(r)));
	mat2 scale_matrix = mat2(vec2(1.0, 0.0),vec2(0.0, TEXTURE_PIXEL_SIZE.x/TEXTURE_PIXEL_SIZE.y));
	vec2 uv = (((UV * 2.0 - 1.0)*3.+1.0)/2.0-vec2(0.5))*scale_matrix;
	vec2 uv_normal = uv * rotation_matrix * inverse(scale_matrix) + vec2(0.5);
	uv = (uv * rotation_matrix + vec2(0.25)*offset*rotation_matrix)*inverse(scale_matrix)+vec2(0.5);
	vec4 shadow;
	vec4 real;
	if(offset != 0.0 && uv.x >= 0.0 && uv.x <= 1.0 && uv.y >= 0.0 && uv.y <= 1.0) {
		vec4 c = texture(TEXTURE, uv);
		shadow = vec4(0.,0.,0.,c.a-0.6);
	}
	if(uv_normal.x >= 0.0 && uv_normal.x <= 1.0 && uv_normal.y >= 0.0 && uv_normal.y <= 1.0) {
		real = texture(TEXTURE,uv_normal);
	}
	COLOR = mix(shadow,real,real.a);
}
Live Preview
Tags
2d, bounds, offset, rotate, rotation, shadow, topdown
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from Bettiold

Related shaders

Comments 10

      1. schneed.

        It seems it's an issue with the Rendering set-up. I had the exact same issues, but when I changed my renderer to Compatibility (top right) it worked.

        This isn't viable - it means you cannot run a lot of other visual effects properly just to make the drop shadow work.

        0
    1. I was able to fix it.

      Just change these lines:

      vec4 shadow;
      vec4 real;
      

      to:

      vec4 shadow = vec4(0,0,0,0);
      vec4 real = vec4(0,0,0,0);
      

      I'm not exactly sure where those colors are coming from without that, though..

      2
      1. the problem was that, in lines 16 and 17, you declare 2 variables (of type vec4) without initializing them (ie, without giving them values). they might be given values if the if-branches of the following if-statements are taken. if that doesn't happen, then there's no guarantee on what values these variables have, which may cause all sorts of problems. in these cases, it's best to initialize the variables, like you did in your comment.

        (this is unlike in GDScript, where var foo: int under the hood initializes foo to zero (you can't have an uninitialized variable in GDScript).

        0
  1. I get error in Line 18, tried to fix it but no success

    Direct floating-point comparison (this may not evaluate to true as you expect). Instead, use abs(a - b) < 0.0001 for an approximate but predictable comparison.

    0
    1. It should just be a warning, not an error.

      You can fix it by changing

      offset != 0.0
      

      to

      offset > 0.0
      
      0
  2. Crumblebit

    If the above shader is not working, you can achieve the same effect with sprites. Just add a Node2D to your scene, call it Shadow and attach a script with this code:

    @export var shadow_offset = Vector2(8,8)
    
    func _process(_delta):
        global_position = get_parent().global_position + shadow_offset
    

    Now you can add as many shadow Sprite2Ds as children to this "Shadow" node as you want. The benefit is that the shadow can be sprites, so no limit to how it looks. And the code is very simple, just offset the global position and inherit the rotation. :)

    2
    1. I think that the idea of the shader is to avoid drawing all the shadow sprites.

      0