Search code examples
c#unity-game-engineuser-interfacegame-development

How can I set Unity UI Shadow effect distance based on a third person camera & directional light?


I'm trying to set the effect distance of a UI shadow based on a third person camera and the directional light in the scene to make it look like the UI has a shadow that moves depending on how the camera is rotated and the directional light, but I can't get the calculations right.

The camera is a normal third person camera that follows the player and orbits around the player.

I've been at this for a couple days, using a combination of the camera's forward, right and up values with the directional light's forward, right and up values. Multiplying and adding them together until it looks right.

This is the best I've found so far, but I still haven't found a good way to incorporate the directional light.

Vector2 effectDistance;
effectDistance.x = Camera.main.transform.right.z;
effectDistance.y = -Camera.main.transform.forward.y * Camera.main.transform.up.z;

if (Camera.main.transform.eulerAngles.x > 180)
{
    effectDistance.y *= -1;
}

shadow.effectDistance = effectDistance * 100 * -directionalLight.right.y;

With this, when moving the camera up, the shadow moves up as well. But it doesn't take the directional light's eulerAngles.y into account yet, so it only follows correctly when it's set to 0.

But I've just found that this code still doesn't work fully even when the directional light's eulerAngles.y is 0...

help

I'm on the verge of giving up to be honest.


Solution

  • Here is what I came up with which seems to work more or less

    My idea was mainly

    • Take the light direction
    • project it onto the ground (basically the shadow)
    • project that onto the camera plane
    • Convert that into the local space of the camera since the global UI is basically like attached to the camera

    So all together something like e.g.

    var lightDirection = directionalLight.transform.forward;
    // basically how the shadow falls on the ground
    var lightOnGroundPlane = Vector3.ProjectOnPlane(lightDirection, Vector3.up);
    // map the shadow direction into the camera plane
    var lightDirectionOnCameraPlane = Vector3.ProjectOnPlane(lightOnGroundPlane, Camera.main.transform.forward);
    // convert it to a direction in Camera local space
    var lightDirectionOnCameraPlaneLocal = Camera.main.transform.InverseTransformDirection(lightDirectionOnCameraPlane);
    
    // finally apply some multiplier (usually that would be for instance the height of the 3D object + you are in pixel space)
    shadow.effectDistance = new Vector2(lightDirectionOnCameraPlaneLocal.x, lightDirectionOnCameraPlaneLocal.y) * 100;