How to calculate Camera “correct” position and distance to Frame all my Scene?

Consider I have N dynamic points on my Scene, I would like to "frame" all them, with my Camera.

How can i calculate (at runtime) Vector3 Position to correctly frame all my scene ?

This is my idea:

1. Calculate Avarage for all my points

2. Move my Camera "up" to correctly "frame" all my points (i don't know how)

3. Use Unity LookAt(myAvaragePoint)

Thanks

So there's effectively an infinite number of points which would qualify. Even if we assume that there's a single distance value which satisfies your problem (as any distance greater than that also works! But we will assume that you want the smallest distance for which this works), that still leaves every possible rotation around which we could orbit the camera and maintain all points in view.

That said, here's the approach I took:

1. Find the average center for all points. A better solution would be to compute the geometric center1 (so 40 points that are located very far out don't have any more impact than one point far out) but the raw average was easier for this sample and sufficient for the "hard part" (your step 2).

2. Move the camera to this point, then move it up 1 unit, and then move it backwards (-1 * transform.forward) 1 unit, then look at the average center point.

• This offsets the camera from the average so that when we start moving the camera away from the group we don't have to reorient the view: we already know this is the center and we will just move the camera backwards until all points are visible
3. Find the on-screen pixel coordinates of each point in the data set. If any of the values are negative, they are outside our view! Additionally, if the X or Y values are greater than the size of the screen, they are outside of the view in that direction too.

4. Loop: move the camera backwards 1 unit, recheck on-screen values. Repeat until all points are on screen.

And that gives us this:

using UnityEngine;

public class FocusInOnAll : MonoBehaviour {
public Transform[] allPts;

// Use this for initialization
void Start () {
Vector3 averageCenter = Vector3.zero;
bool wasAnyPointOutside = true;
foreach(Transform t in allPts) {
averageCenter += t.position;
Vector3 v = Camera.main.WorldToScreenPoint(t.position);
Debug.Log("Point at: " + v);
if(v.x < 0 || v.y < 0 || v.z < 0 || v.x > Screen.width || v.y > Screen.height) {
wasAnyPointOutside = true;
}
}
Debug.Log("Center: " + averageCenter / allPts.Length);
Camera.main.transform.position = (averageCenter / allPts.Length) + Vector3.up - Camera.main.transform.forward;
Camera.main.transform.LookAt((averageCenter / allPts.Length));
while(wasAnyPointOutside) {
wasAnyPointOutside = false;
Camera.main.transform.position = Camera.main.transform.position - Camera.main.transform.forward;
foreach(Transform t in allPts) {
Vector3 v = Camera.main.WorldToScreenPoint(t.position);
if(v.x < 0 || v.y < 0 || v.z < 0 || v.x > Screen.width || v.y > Screen.height) {
wasAnyPointOutside = true;
}
}
}
}
}


1This would be done by keeping track of the minimum and maximum X and Y values, then averaging the min and max together.

If you are not too worried about performance you could probably go for an iterative process. Following the steps you mentioned:

1. compute average position
2. LookAt(average position)
3. move camera along its -transform.forward axis by a fixed step, until the condition is met.

The condition is that all points are within view: one way to do it is to iterate on every point and check if the screenspace position is between 0 and 1 on both axis. (https://docs.unity3d.com/ScriptReference/Camera.WorldToViewportPoint.html)