I'm not sure whether it is the most elegant solution but I found one.
I should have used Tiling and Offset
node to scale and shift the UV of the ground texture. Here's the shader graph:

Right now this shader makes the object project the part of the ground texture it is placed on onto itself. It seems to be perfectly aligned with the texture.
Ground position is calculated in code:
[RequireComponent(typeof(SpriteRenderer))]
public class displaced_shadow : MonoBehaviour
{
SpriteRenderer sr;
Rect rect;
MaterialPropertyBlock material_properties;
float pixels_per_unit;
public Vector2 Size //get size of the texture in Unity units
{
get { return new Vector2(rect.width * transform.localScale.x/ pixels_per_unit, rect.height * transform.localScale.y/ pixels_per_unit); }
}
private void Awake()
{
sr = GetComponent<SpriteRenderer>();
rect = sr.sprite.rect;
pixels_per_unit = sr.sprite.pixelsPerUnit;
material_properties = new MaterialPropertyBlock();
}
// Update is called once per frame
void Update()
{
Vector2 relative_position = CalculateRelativePostion();
FeedMaterial(relative_position);
}
private Vector2 CalculateRelativePostion() //calculate the position relative to the background in [0,1] range
{
if (background_floor.This == null) return Vector3.zero;
Vector2 bg_position = background_floor.This.Position; //get the size and position of the background texture
Vector2 bg_size = background_floor.This.Size;
float origin_x1 = transform.position.x - Size.x/2f; //calculate the top left corner positions in case textures have their origins in the center
float origin_y1 = transform.position.y - Size.y / 2f;
float origin_x2 = bg_position.x - bg_size.x / 2f;
float origin_y2 = bg_position.y - bg_size.y / 2f;
float x = (origin_x1 - origin_x2) / bg_size.x; //get the [0,1] position of the object relative to the ground texture
float y = (origin_y1 - origin_y2) / bg_size.y;
return new Vector2(x, y);
}
private void FeedMaterial(Vector2 relative_position)
{
sr.GetPropertyBlock(material_properties);
material_properties.SetVector("_GroundPosition", relative_position);
sr.SetPropertyBlock(material_properties);
}
}