3

I am working on a 2d top-down game. I have an object that moves on a single large texture and casts shadow on it (just a blurry dark sprite). However I found out that since my texture has bumps, rocks, creases etc. it looks like the shadow sprite just hovers above the ground. So I figured out that I could use my texture as a displacement map to distort the shadow sprite depending on the surface of the texture. I'm trying to implement that in Shader Graph but I can't manage to get proper position and scale of the underlying texture to project the shadow on.
I tried to implement a simplified version of the shader that precisely reproduces the part of the Ground Texture that it hovers above.

enter image description here

In this case scaling seems to be off, the resulting texture is a lot smaller than the original one. I'm not sure about the position but it seems to be off as well. Am I missing out something? Or is there an easier appreach to implement this?

Thanks in advance.

Mozmarto
  • 73
  • 1
  • 7

1 Answers1

2

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:

enter image description here

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);
    }
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Mozmarto
  • 73
  • 1
  • 7