问题
Objective: Move the camera position ONLY on the Z axis so the frustrum fit 2 objects.
Conditions:
- One of the objects will be allways aligned with the camera X position
- Camera is set on perspective mode, not ortographic.
- the 2 spheres have no parent
The result seen in ortographic mode from top-view should look like this:
What I've done so far:
Using trigonometry this can be seen it as:
Knowing that, the objective is to find the Adjacent side, which will be the distance between the camera and the black point that will still fit the yellow one.
TECHNICALLY this piece of code should find the adjacent value:
private float CalculateMaxZoomDistanceToBall()
{
//Calculate angle from camera, should be divided of 2 cause it's placed on the middle of the line
Camera currentCamera = cameraComp;
angleDegrees = currentCamera.fieldOfView / 2; //(degrees)
//pass the angle to radians
angleRadians = angleDegrees * Mathf.Deg2Rad;
//Calculate the SinAngle
sinAngle = Mathf.Sin(angleRadians);
//Calculate Opposite
opposite = Mathf.Abs(blackPoint.transform.localPosition.x - yellowPoint.transform.position.x);
//Calculate hypotenuse
hypotenuse = opposite / sinAngle;
//Calculate CosX
cosAngle = Mathf.Cos(angleRadians);
//Calculate adjacent distance
adjacent = cosAngle * hypotenuse;
return adjacent;
}
as the camera object is positioned at 0, I simply add the return value to the gameObject.transform.position.z
And someone could say "but this is looking for the vertical FOV, you need the horizontal one", okey, I've also tried with the horizontal one, finded with:
float vFOVrad = currentCamera.fieldOfView * Mathf.Deg2Rad;
float cameraHeightAt1 = Mathf.Tan(vFOVrad * 0.5f);
float hFOVrad = Mathf.Atan(cameraHeightAt1 * currentCamera.aspect) * 2;
hFOV = hFOVrad * Mathf.Rad2Deg;
And it's not working, in some cases the camera position is to far of the espected position, sometimes it fits well and others it just goes to close.
Any help will be apreciated, thank you.
回答1:
I would avoid working with angles and work in the world of vectors and planes.
Determine which side of the camera the yellow point is on:
Camera cam = cameraComp;
Transform camTransform = cam.transform;
Vector3 yellowPos = yellowPoint.transform.position;
// <0 if on left, >0 if on right
float camDirection = Vector3.Dot(camTransform.right, yellowPos - camTransform.position);
// if it's directly straight ahead, do nothing.
if (Mathf.Approximately(camDirection, 0f)) return;
Find a ray for the camera viewport edge on the same side of the yellow point. Height in the viewport won't matter.
Ray edgeRay = cam.ViewportPointToRay(camDirection < 0f ? Vector3.zero : Vector3.right);
Define an algebraic plane (not physics plane) normal to the camera's right and going through the position of the yellow point:
Plane yellowPlane = new Plane(camTransform.right, yellowPos);
Find the intersection of the ray and plane using algebraic raycast (not physics raycast):
float raycastDistance;
if (! yellowPlane.Raycast(edgeRay, out raycastDistance)) return; // should not return
Vector3 raycastPoint = edgeRay.GetPoint(raycastDistance);
Find the difference from the intersection point to the yellowPoint position, and do a dot product with the camera's forward direction to find how to move the camera along its forward direction:
float forwardDelta = Vector3.Dot(camTransform.forward, yellowPos - raycastPoint);
camTransform.Translate(0f, 0f, forwardDelta);
So, altogether:
Camera cam = cameraComp;
Transform camTransform = cam.transform;
Vector3 yellowPos = yellowPoint.transform.position;
// <0 if on left, >0 if on right
float camDirection = Vector3.Dot(camTransform.right, yellowPos - camTransform.position);
// if it's directly straight ahead, do nothing.
if (Mathf.Approximately(camDirection, 0f)) return;
Ray edgeRay = cam.ViewportPointToRay(camDirection < 0f ? Vector3.zero : Vector3.right);
Plane yellowPlane = new Plane(camTransform.right, yellowPos);
float raycastDistance;
if (! yellowPlane.Raycast(edgeRay, out raycastDistance)) return; // should not return
Vector3 raycastPoint = edgeRay.GetPoint(raycastDistance);
float forwardDelta = Vector3.Dot(camTransform.forward, yellowPos - raycastPoint);
camTransform.Translate(0f, 0f, forwardDelta);
The good thing about this approach is that it will work regardless of the orientation of the camera, or the relative position of the point from the camera.
来源:https://stackoverflow.com/questions/60001501/maximize-zoom-distance-to-fit-2-objects