Я пытаюсь решить следующую проблему:
У меня есть объект сферы в мировом пространстве, и его координаты поверхности сопоставлены с геокоординатами.Я использую перспективную проекционную камеру, чтобы смотреть на эту поверхность.В качестве входных данных у меня есть прямоугольник в географических координатах (или точка, имитируемая этим прямоугольником с topLeft == bottomRight
координаты, крен, высота тона и пользователь, также может определить некоторые отступы для определения областей видимого вида, которые могут быть покрыты элементами графического интерфейса. Мне нужновычислите положение и расстояние камеры, чтобы гео-прямоугольник (точка) был полностью виден в поле зрения камеры. Это нормально, если я смотрю на поверхность прямо сверху (без наклона камеры). Вот эскиз:
Входной гео-прямоугольник показан зеленым цветом и повернут на 45 градусов. Также имеются отступы в измерениях x и y, охватывающие 0,5f пространства экрана в обоих направлениях.Вот как я вычисляю свойства камеры:
void ComputeParams(const GeoRect& rectangle, const Point2& screenCenter, float rotation, const Margin& padding)
{
float fFovX = 0.0f, fFovY = 0.0f;
camera.GetCamera().GetFOV().GetFovXY(fFovX, fFovY);
ASSERT(!FLOAT_EQUAL(padding.left + padding.right, 1.0f, 0.01f));
ASSERT(!FLOAT_EQUAL(padding.top + padding.bottom, 1.0f, 0.01f));
// Clamp padding to < 1.0f to prevent division by 0 later
const float horizontalPadding = std::clamp(padding.left + padding.right, 0.0f, 0.99f);
const float verticalPadding = std::clamp(padding.top + padding.bottom, 0.0f, 0.99f);
const float angle = DegToRad(rotation);
const float sinAngle = Math::Sin(angle);
const float cosAngle = Math::Cos(angle);
const float tanHalfFovX = Math::Tan(fFovX * 0.5f);
const float tanHalfFovY = Math::Tan(fFovY * 0.5f);
const auto correctionX = Math::Cos(DegToRad(rectangle.GetCenter().lon / 100000.f));
const auto alignedWidth = rectangle.GetWidth() * correctionX;
const auto alignedHeight = rectangle.GetHeight();
float rotatedWidth = alignedHeight * std::abs(sinAngle) + alignedWidth * std::abs(cosAngle);
float rotatedHeight = alignedWidth * std::abs(sinAngle) + alignedHeight * std::abs(cosAngle);
rotatedWidth /= (1.0f - horizontalPadding);
rotatedHeight /= (1.0f - verticalPadding);
Сначала я вычисляю выровненный размер прямоугольника, он же размер, когда мой вид выровнен с пространством гео координат, затем я вычисляю размер прямоугольника, если он повернут итакже добавление отступа к уравнению. Затем я вычисляю расстояние камеры, где мне нужно переместить его, чтобы увидеть прямоугольник с нужным падди.ng:
const float cameraDistX = (rotatedWidth * 0.5f) / tanHalfFovX; // Distance to fit rotated rectangle in view frustum in X dimension
const float cameraDistY = (rotatedHeight * 0.5f) / tanHalfFovY; // Distance to fit rotated rectangle in view frustum in Y dimension
// Map computed camera distance to allowed range
const auto distanceRange = GetMinMaxDistance();
const auto distance = std::clamp(std::max(cameraDistX, cameraDistY), distanceRange.min, distanceRange.max);
float newWidth{ rotatedWidth }, newHeight{ rotatedHeight };
if (cameraDistY < distance)
{
newHeight = 2.f * distance * tanHalfFovY;
}
if (cameraDistX < distance)
{
newWidth = 2.f * distance * tanHalfFovX;
}
Затем я вычисляю положение lookAt, чтобы прямоугольник располагался в запрашиваемой части экрана, я также использую значение screenCenter
, которое в основном является точкой фокусировки камеры, вокруг которойон вращается, но давайте представим, что это всегда 0.5f, 0.5f (середина экрана):
const float newPaddingHorizontal = newWidth * (-padding.left + padding.right);
const float horizontalShift = ((newWidth - newPaddingHorizontal) * 0.5f - newWidth * 0.5f);
const float newPaddingVertical = newHeight * (-padding.bottom + padding.top);
const float verticalShift = ((newHeight - newPaddingVertical) * 0.5f - newHeight * 0.5f);
// pitch, roll, yaw
Point3 r(-MATH_HALFPI, angle, 0.0f);
auto lookAt = rectangle.GetCenter();
Matrix4 rotationMatrix;
rotationMatrix.SetIdentity();
rotationMatrix.Rotate(r);
auto vMoveX = rotationMatrix.GetAxis(0);
vMoveX *= (horizontalShift + newWidth * (0.5f - screenCenter.x));
vMoveX.x /= correctionX;
auto vMoveY = rotationMatrix.GetAxis(1);
vMoveY *= (verticalShift + newHeight * (0.5f - screenCenter.y));
vMoveY.x /= correctionX;
const auto vMove = vMoveX + vMoveY;
lookAt.lat = lookAt.lat - (int32_t)vMove.x;
lookAt.lon = lookAt.lon + (int32_t)vMove.z;
return newCameraPosition;
Это все работает хорошо, но теперь мне нужно вычислить положение и расстояние камеры lookAt, поэтому с учетом геоПрямоугольник (точка) находится в области без отступов, как в предыдущем сценарии, но теперь у меня также определен шаг камеры:
, но я не совсемуверен, как этого добиться.Есть идеи?