Godot Version
4.4 stable
Question
Hi there! I’m trying to make a shader that draws a texture as either a shadow on land or a reflected copy on water—but I only know how to do this on a Sprite2D. When I switch to a CanvasGroup, I lose access to the node’s size inside the shader, and I can’t figure out how to query it.
I know CanvasGroups render their children into an off-screen buffer, but I need the width×height of that buffer in pixels, so I can translate my UV
into the right world-pixel position for sampling my terrain mask.
Below is my working Sprite2D shader (where I simply use textureSize(TEXTURE, 0)
to get the sprite’s size). Any ideas how to get the CanvasGroup’s dynamic size either in GDScript or inside its shader?
shader_type canvas_item;
uniform sampler2D terrain_mask;
uniform vec2 map_size_pixels = vec2(4096.0, 2304.0);
uniform vec2 mech_world_pos_pixels;
uniform bool center_pos = true;
uniform float lower_water_threshold : hint_range(0.0, 1.0) = 0.0;
uniform float upper_water_threshold : hint_range(0.0, 1.0) = 1.0;
void fragment() {
vec4 base = COLOR;
if (base.a < 0.01) {
discard; // skip fully transparent pixels
}
// 2) figure out which pixel in terrain_mask we need
// mech_world_pos_pixels is the top-left corner of your shadow sprite in world-space
vec2 texture_size = vec2(textureSize(TEXTURE, 0));
vec2 world_pixel = mech_world_pos_pixels + UV * texture_size;
// center the position if we're asked to
if (center_pos) {
world_pixel -= texture_size * 0.5;
}
// 3) convert to [0,1] UVs for the 4096×2304 mask
vec2 mask_uv = world_pixel / map_size_pixels;
// 4) sample terrain_mask
float t = texture(terrain_mask, mask_uv).r;
bool is_water =
(lower_water_threshold > 0.0 && t <= lower_water_threshold) ||
(upper_water_threshold < 1.0 && t >= upper_water_threshold);
// 5) pick shadow or reflection
if (is_water) {
// on water → reflection
COLOR.a = base.a * 0.5;
} else {
// on land → shadow
COLOR = vec4(0.0, 0.0, 0.0, base.a * 0.5);
}
}