How to achieve a black rectangle that covers the entire world except for certain rectangular areas?

Godot Version

4.6

Question

Hello! I’m trying to achieve something but I can’t get where I want. The project I’m working on is a 2D pixel art game.

My goal is to have a black rectangle that covers the entire world (or the entire screen) and only allows to see in a specific area. Think of different rooms: when the player is in one room, only that room is visible and the rest of the world is black.

I’ve tried different approaches but I’m not sure if I’m going in the right direction.

Approach A: CanvasModulate + PointLight2D

By setting up a CanvasModulate I already have an infinite plane that covers everything. PointLight2D lets me clip that plane. The problem is that it seems to require a texture, and I cannot just set a rectangle, I need an image for that. I guess I could just have a 1x1 pixel image and scale it according to my needs?

Also, it somehow doesn’t feel right to use a lighting system for this.

Approach B: Shaders + ColorRect

I tried something with shaders and it kinda does the trick, except that I don’t know how to make the shader “world aware”. It seems to understand coordinates relative to the screen / camera, but if I set a global position from my world, it does not translate correctly.

Also, I think it needs a ColorRect to work, meaning that instead of having an “infinite” plane like I have with CanvasModulate, I have to place and resize the ColorRect myself.

So… Any ideas or suggestions on this? Thanks!

Is it always a single rectangular area that is visible?

Yep, of different sizes but always a rectangle

You could probably use z sorting to make this happen. You could have a black color rect on Z layer 0. Then have a script that makes the current active room z layer 1 or if it’s in active, z index -1.

Also that black color rect could just be attached to your camera and set to the resolution size so it’s only covering what’s actually needed and not just a massive black square.

Mmm that’s clever. But it wouldn’t work in my case. I have elements from a room that can end up in another room, so they would just disappear if I do this. But thanks anyway!

Run a shader on a color rect that covers the entire screen. Pass the visible rect to the shader as an uniform and set alpha of pixels that are inside rect to zero.

How can I translate global positions in the world to screen positions in the shader?

You don’t need to. Pass the visible rect in global space and just transform the pixel position to global space by multiplying it with MODEL_MATRIX.