温馨提示:本文翻译自stackoverflow.com,查看原文请点击:c# - Maximize zoom distance to fit 2 objects
c# unity3d camera trigonometry fieldofview

c# - 最大化缩放距离以适合2个对象

发布于 2020-03-28 23:34:37

目标:在Z轴上 移动摄像机位置,以使截锥体适合2个对象。

条件:

  • 其中一个对象将始终与摄影机X位置对齐
  • 相机设置为透视模式,而不是正交模式
  • 这两个领域没有父母

从顶视图在地形学模式下看到的结果应如下所示: enter image description here

到目前为止,我所做的是:

使用三角函数可以将其视为:

enter image description here

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.

查看更多

查看更多

提问者
Lotan
被浏览
104
Ruzihm 2020-02-01 00:14

我会避免使用角度,而不会使用向量和平面。

确定黄点在相机的哪一侧:

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);

找到相交点到yellowPoint位置的差,然后对照相机的向前方向进行点积运算,以查找如何沿照相机的向前方向移动它:

float forwardDelta = Vector3.Dot(camTransform.forward, yellowPos - raycastPoint);

camTransform.Translate(0f, 0f, forwardDelta);

因此,总共:

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);

这种方法的好处是,无论相机的方向如何,还是相机上的点的相对位置如何,它都可以工作。