How in the world does REGION_RECT work

Godot Version

4.5

Question

I’m new to shaders and having a very hard time creating a shader that applies to a region of an AtlasTexture. UV refers to the entire texture. Supposedly, I can use REGION_RECT to get around this, but I cannot for the life of me understand how. I’ve tried utilizing the example provided in the PR: Expose built-in region information by dsmtE · Pull Request #90436 · godotengine/godot · GitHub, but either I’m utterly misunderstanding it or it’s just not applicable code to my use-case.


vec2 uv_region = (UV - REGION_RECT.xy) / REGION_RECT.zw; gives me the whole texture, squashed. JUST vec2 uv_region = (UV - REGION_RECT.xy); seems to return the region starting at 0, 0 (aka the top left corner) instead of the one specified.

I can’t find any other examples of it’s use; from what I understand it’s new to 4.5, so that’s understandable. Can anyone explain to me how I can use it to get the UV of just that region? Also, are both these inspector region editors applicable for REGION_RECT? It’s a bit confusing that there’s one that’s part of the AtlasTexture resource and then another entirely separate one that does the same thing.


What exactly are you trying to achieve?

I’m working on a pixel-perfect tree-swaying-in-the-wind shader. I know pixel-perfect could just be gotten via the project viewport settings, but using that setting ruins the smooth character movement that I’m attempted to preserve. So I’ve landed on a middle-ground of a shader. My background assets (like trees) are on spritesheets and the shader I’ve made depends on using the UV.y to determine how far pixels are displaced (causing the sway to increase with the height of the tree). This doesn’t work well with an AtlasTexture and was causing a lot of problems, so I wanted a way to get the UV of just the selected region.

Since I posted this, I’ve actually ended up just separating the trees into individual image files, as I think my sanity is worth it. The shader works great when it’s not on an AtlasTexture.

REGION_RECT is the uv of the region. Check if UV is inside that rect and act accordingly.

I had similar problem in my shadow shader. I’ve solved this with single if checking if uv after transformation to region is within 0-1 range. Also yours code is missing reverse operation to transform local UV back to original form (line in my code: vec2 atlas_uv = rect.xy + local_uv * rect.zw;). It is important, as you are sampling whole texture.

Here is my code (it’s wip, but working):

shader_type canvas_item;
render_mode blend_mix;

uniform vec2 deform = vec2(2.0, 2.0);
uniform vec2 offset = vec2(0.0, 0.0);
uniform vec4 modulate : source_color;
uniform float x_buffer = 2.0;
uniform float y_buffer = 2.0;

void vertex() {
	VERTEX.x *= x_buffer;
	VERTEX.y *= y_buffer;
}

void fragment() {

	vec4 rect = REGION_RECT;

	// --- atlas → region-local (0..1)
	vec2 local_uv = (UV - rect.xy) / rect.zw;

	// --- counter vertex scaling
	local_uv = local_uv * vec2(x_buffer, y_buffer)
	         - (vec2(x_buffer, y_buffer) - 1.0) * 0.5;

	// --- region bounds check
	if (
		local_uv.x < 0.0 || local_uv.x > 1.0 ||
		local_uv.y < 0.0 || local_uv.y > 1.0
	) {
		COLOR = vec4(0.0);
	} else {
		vec2 atlas_uv = rect.xy + local_uv * rect.zw;
		COLOR = texture(TEXTURE, atlas_uv);
	}

		// --- region pixel size (NOT atlas size)
		vec2 atlas_size = vec2(textureSize(TEXTURE, 0));
		vec2 region_ps = rect.zw / atlas_size;

		// ================= SHADOW =================
		vec2 shadow_uv = local_uv;
		
		float decalx = (shadow_uv.y - 0.5) * deform.x;
		float decaly = (shadow_uv.y - 0.5) * deform.y;

		shadow_uv += offset;
		shadow_uv += vec2(decalx, decaly);

		// shadow bounds clamp
		vec2 shadow_atlas_uv = rect.xy + shadow_uv * rect.zw;
		float shadow_alpha = texture(TEXTURE, shadow_atlas_uv).a;

		vec4 shadow = vec4(modulate.rgb, shadow_alpha * modulate.a);

		if (
			shadow_uv.x >= 0.0 && shadow_uv.x <= 1.0 &&
			shadow_uv.y >= 0.0 && shadow_uv.y <= 1.0
		) {
			// ================= COMPOSE =================
			COLOR = mix(shadow, COLOR, COLOR.a);
		}
}

it is modified version of: 2D shadow out of bounds - Godot Shaders