With a perspective camera: Distance independent size gameobject

Hi!, I need to make a GameObject approach the camera with a custom screen-space size (i need a object to have a “max size” and stop growing after a set distance). Ideally, i’d want a simple formula so i could scale the object depending its distance from the camera.
After hours of trying and searching I found this simple formula:

scale = focalLength / (focalLength + distance);

I tried applying this inversed so the object keeps its size, and this nearly works, but with not enough accuracy because i can’t figure out what “focalLength” is exactly (at least this is my best guess, and certainly is not FOV).

Note that i’m struggling to wrap my head around this because i’m not comfortable around projection and transform matrices, so a noob-math friendly answer is mostly welcome.

Anyone can help me out with this or provide a better method?.

Thanks!

Instead of figuring out the math, just pick how big you want it on screen and use the camera functions to “measure” it in world units.

public class LockToScreenSize : MonoBehaviour {

    public float sizeOnScreen;

    void Update () {
        Vector3 a = Camera.main.WorldToScreenPoint(transform.position);
        Vector3 b = new Vector3(a.x, a.y + sizeOnScreen, a.z);

        Vector3 aa = Camera.main.ScreenToWorldPoint(a);
        Vector3 bb = Camera.main.ScreenToWorldPoint(b);

        transform.localScale = Vector3.one * (aa - bb).magnitude;
    }
}

I don’t exactly know what focal length is but googling revealed this relation between FOV and focal length:
FOV = 2arctan * (x / (2focalLength))
(from http://kmp.bdimitrov.de/technology/fov.html)

The calculator on this page uses the following code to get the focal length f from a given FOV:

function calcf(fov, crop)
{
  if (fov == "") return "";
  if ((fov <= 0) || (fov >= 180)) return "error";
  f = (x / (2 * Math.tan(Math.PI * fov / 360)));
  return f / crop;
}

Maybe this is a pointer in the right direction!
Hope that helps :slight_smile:

If you don’t care about actual pixel sizes, you can ignore the camera’s FoV - the perceived scale change of an object is just the quotient objectAtDistanceA/objectAtDistanceB (“distance” being the local z distance to the camera, i.e. the distance vector from camera to object projected onto camera.transform.forward, as opposed to just the distance vector).

If you want to know how big your object is, or want to make an object exactly x pixels large, so you can keep it at a certain pixel height, you can use the formula @DelStrega pointed out to help you compute that size. So the height in pixels of an object at z-distance “distance” is just:

objectPixelHeight=height/distance/(2*Mathf.Tan(Camera.main.fieldOfView/2*Mathf.Deg2Rad))*Camera.main.pixelHeight;

Note all this works correctly only for planar objects - as @Bunny83 pointed out, the perspective (and therefore the object’s perceived size) changes if it has a depth.

Of course you can calculate the size at a certain distance, but it won’t help much. A perspective camera always shows closer parts bigger. If you calculate the size for the objects center, it will still be bigger. The closest bounding rectangle the object would fit in is much more complicated to calculate.

You might want to use a second camera and draw it orthographic? Keep in mind to setup the render order (camera’s depth parameter) and the second camera should have clearflags none.

I’m not sure if the depth buffer will work in this case. Usually gizmos are displayed anyway. If you want to draw it no matter if it would be covered by something else or not, just set the Clear flags to depth-only

In case someone comes across it, this solution worked in my case:
Camera can zoom in / out and be moved on x/y axis, and some objects (game dialog) must be opened at fixed size regardless of camera zoom.

If using orthographic camera, just use the orthographic size (assuming you start at 100), and use it as scale for the fixed size object (if not, you can easily convert it to % from 100 if object scale isn’t 100 by default).

say camera at size 120 (farther from plane, object must be larger, and will be scaled to 120% of it’s original size).

public void updateScale() {

		float size = Camera.main.orthographicSize;

		Vector3 scale = new Vector3 (size, size, size);
		objectToScale.transform.localScale = scale;

}