### Tested versions

Reproducible in 4.2-stable 

### System information

Godot v4.2.stable (46dc27791) - Windows 10.0.22621 - Vulkan (Forward+) - dedicated NVIDIA GeForce GTX 1660 Ti (NVIDIA; 31.0.15.1640) - AMD Ryzen 7 4800H with Radeon Graphics (16 Threads)

### Issue description

If you have a tile_set with an occlusion layer, and you remove a tile with an occlusion shape at runtime, the occlusion shape will remain.

![image](https://github.com/godotengine/godot/assets/6132794/c9eed82b-94c4-4654-b33a-1a9c5be4d45c)

![image](https://github.com/godotengine/godot/assets/6132794/c43cc34b-d307-416e-8e3f-bf49d1421ae7)



### Steps to reproduce

- Setup a scene with CanvasModulate and a 2D Light
- Create a tilemap & tileset
- Add an occlusion layer
- Give one or more tiles an occlusion shape
- Make a script run sometime during process to randomly remove and add the tiles with occlusion shapes, and tiles without.
You should now see errant shadows.

Or, in the MRP, just play the scene and you should see the problem once tiles alternate.

### Minimal reproduction project (MRP)

[OccluderProblem.zip](https://github.com/godotengine/godot/files/13552364/OccluderProblem.zip)

HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_constraints(const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> &p_constraints); 
	HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_connect(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true); 
	HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_path(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true); 
	HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_pattern(const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains = true); 
	
	TileMapCell get_cell(const Vector2i &p_coords, bool p_use_proxies = false) const;
	
	void set_tile_data(DataFormat p_format, const Vector<int> &p_data);
	Vector<int> get_tile_data() const;
	void notify_tile_map_change(DirtyFlags p_what);
	void internal_update();
	
	
	void set_cell(const Vector2i &p_coords, int p_source_id = TileSet::INVALID_SOURCE, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = 0);
	void erase_cell(const Vector2i &p_coords);
	int get_cell_source_id(const Vector2i &p_coords, bool p_use_proxies = false) const;
	Vector2i get_cell_atlas_coords(const Vector2i &p_coords, bool p_use_proxies = false) const;
	int get_cell_alternative_tile(const Vector2i &p_coords, bool p_use_proxies = false) const;
	TileData *get_cell_tile_data(const Vector2i &p_coords, bool p_use_proxies = false) const; 
	void clear();
	
	Ref<TileMapPattern> get_pattern(TypedArray<Vector2i> p_coords_array);
	void set_pattern(const Vector2i &p_position, const Ref<TileMapPattern> p_pattern);
	
	void set_cells_terrain_connect(TypedArray<Vector2i> p_cells, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true);
	void set_cells_terrain_path(TypedArray<Vector2i> p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true);
	
	TypedArray<Vector2i> get_used_cells() const;
	TypedArray<Vector2i> get_used_cells_by_id(int p_source_id = TileSet::INVALID_SOURCE, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE) const;