Максимизируйте расстояние увеличения, чтобы соответствовать 2 объектам - PullRequest
2 голосов
/ 31 января 2020

Объектив: Переместить положение камеры ТОЛЬКО по оси Z, чтобы в усечку поместились 2 объекта.

Условия:

  • Один из объектов будет всегда выровнен с положением камеры X
  • Камера установлена ​​в режим перспективы , не ортографически c.
  • у двух сфер нет родителя

Результат при просмотре в режиме ortographi c сверху должен выглядеть следующим образом: enter image description here

Что я сделал до сих пор:

Используя тригонометрию, это можно увидеть как:

enter image description here

Зная это, цель состоит в том, чтобы найти Смежную сторону , которая будет расстоянием между камерой и черной точкой, которая все еще будет соответствовать желтой.

TECHNICALLY этот фрагмент кода должен найти смежное значение:

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

, поскольку объект камеры расположен в 0, я просто добавляю возвращаемое значение к gameObject.transform.position.z

И кто-то мог бы сказать ", но это ищет вертикальное поле зрения, вам нужно горизонтальное поле зрения" , хорошо, я также пробовал с горизонтальным полем, оканчивающимся на:

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;

И это не работает, в некоторых случаях положение камеры слишком далеко от предполагаемого положения, иногда оно хорошо подходит, а в других просто закрывается.

Любая помощь будет оценена, спасибо.

1 Ответ

1 голос
/ 31 января 2020

Я бы избегал работы с углами и работы в мире векторов и плоскостей.

Определите, на какой стороне камеры находится желтая точка:

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

Определите алгебраическую c плоскость (не физическую плоскость), перпендикулярную камере справа и проходящую через положение желтой точки:

Plane yellowPlane = new Plane(camTransform.right, yellowPos);

Найдите пересечение луча и плоскости с помощью алгебры c raycast (не физика raycast):

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

Хорошая вещь в этом подходе состоит в том, что он будет работать независимо от ориентации камеры или относительного положения точки от камеры.

...