How to disable culling when CanvasItem goes out of screen?

Godot Version

4.3.beta3

Question

How to disable culling when CanvasItem goes out of screen?

Test project is the following:

image

Code for Icon:

extends Sprite2D

const SPEED: float = 300.0

func _process(delta: float) -> void:
	var input_vector: Vector2 = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
	position += input_vector * SPEED * delta

Shader applied to Icon:

shader_type canvas_item;

void vertex() {
	VERTEX += vec2(100.0, 0.0);
}

The shader applies a 100 pixels translation to the right to the Icon sprite. The thing is, when the sprite goes out of screen, the shader is not applied anymore, resulting in an abrupt cut to the motion like so:

vid

How to force the engine to render the sprite and apply the shader to the CanvasItem even when it is out of the screen?

Maybe the VisibleOnScreenNotifier2D and VisibleOnScreenEnabler2D nodes.

Thanks for your response, I tried them without success. As a understand them, VisibleOnScreenEnabler2D only purpose is to set/unset the Node.process_mode property and VisibleOnScreenNotifier2D send signals when node goes in and out of screen.

Hmm. Not sure. On redit someone changed the viewport/camera to get results.
https://www.reddit.com/r/godot/comments/ses0nk/2d_sprites_not_glowing_when_out_of_screen/

1 Like

That’s exactly the same problem I’m having! Based on the comments in the thread it seems like changing viewport/camera scale didn’t work though.

I actually found an answer to this problem in this thread: https://www.reddit.com/r/godot/comments/17zdksu/comment/k9zucsi/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button

The idea is to use the RenderingServer object to tweak the rendering of the CanvasItem.

In the case the CanvasItem is a Sprite2D, it works using this simple line in the sprite script:

func _ready() -> void:
	RenderingServer.canvas_item_set_custom_rect(get_canvas_item(), true, get_viewport_rect())

However for a TileMap it doesn’t work, as explained by @mrcdk:

Looks like the Tilemap rendering code creates another canvas_item directly in the rendering server and, AFAICT, it’s not exposed to the scripting side.
godot/scene/2d/tile_map.cpp at 80de898d721f952dac0b102d48bb73d6b02ee1e8 · godotengine/godot · GitHub

Actually my problem was the same as the one in the Reddit thread:

How to dissociate the collision shape of a CanvasItem from the actual rendering of the node. In other words, how to apply a transform to the rendering of the CanvasItem that isn’t applied to its collision shape.

The solution I tried to use, as in the Reddit thread, was to apply a shader on the CanvasItem. But this won’t work for a TileMap, as explained above.

There is another solution that doesn’t use shaders, provided in the thread once more by @mrcdk. It is the following:

Apply a custom transform directly to the CanvasItem in the RenderingServer. Here is an example to apply a simple translation to the node:

var canvas_item = get_canvas_item()
var transform = global_transform
transform.origin.x += 100
RenderingServer.canvas_item_set_transform(canvas_item, transform)

This code has to be executed every time the CanvasItem position change, because the transform associated with the CanvasItem inside the RenderingServer will then be updated accordingly to the new position (without translation).

I did test this solution and it worked to dissociate the collision shape and the actual rendering on screen of the CanvasItem.

Last thing, don’t be fooled by the Visible Collision Shapes option:

Enabling Visible Collision Shapes in the Debug menu will still show the collision shapes in the wrong position (the position where the tile is drawn) as they are part of the visual side of the TileMap
@mrcdk

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.