Как получить координаты экрана xy из мировых координат xyz? - PullRequest
1 голос
/ 31 марта 2019

Я создаю простое приложение, которое размещает маркер на вашем экране в верхней части определенных ориентиров в реальном мире, собираясь наложить маркеры на изображение с камеры. У меня есть широта / долгота / высота как для устройства просмотра, так и для мировых ориентиров, и я конвертирую их в координаты ECEF. Но у меня проблемы с математикой 3D-проекции. Кажется, что точка всегда находится в середине экрана ... может быть, мое масштабирование где-то неверно, поэтому похоже, что оно едва перемещается из центра?

Просмотр GPS-координат устройства:

GPS:
  lat: 45.492132
  lon: -122.721062
  alt: 124 (meters)

ECEF:
  x: -2421034.078421273
  y: -3768100.560012433
  z: 4525944.676268726

Ориентир GPS координаты:

GPS:
  lat: 45.499278
  lon: -122.708417
  alt: 479 (meters)

ECEF:
  x: -2420030.781624382
  y: -3768367.5284123267
  z: 4526754.604333807

Я пытался следовать математике из здесь , чтобы создать функцию для получения экранных координат из трехмерных координат точек.

Когда я помещаю эти точки ECEF в мою функцию проецирования с окном просмотра 1440x335, я получаю: x: 721, y: 167

Вот моя функция:

function projectionCoordinates(origin, destination) {
  const relativeX = destination.x - origin.x;
  const relativeY = destination.y - origin.y;
  const relativeZ = destination.z - origin.z;

  const xPerspective = relativeX / relativeZ;
  const yPerspective = relativeY / relativeZ;

  const xNormalized = (xPerspective + viewPort.width / 2) / viewPort.width;
  const yNormalized = (yPerspective + viewPort.height / 2) / viewPort.height;

  const xRaster = Math.floor(xNormalized * viewPort.width);
  const yRaster = Math.floor((1 - yNormalized) * viewPort.height);

  return { x: xRaster, y: yRaster };
}

Я считаю, что точка должна быть размещена намного выше на экране. В той статье, на которую я ссылался, упоминаются матрицы 3х4, за которыми я не мог следить (не знаю, как построить матрицы 3х4 из точек 3D). Возможно, это важно, особенно потому, что мне в конечном итоге придется учитывать наклон устройства (глядя вверх или вниз с помощью телефона).

Если это необходимо, вот моя функция для преобразования координат широты / долготы / высоты в ECEF (скопировать / вставить из другого ответа SO):

function llaToCartesion({ lat, lon, alt }) {
  const cosLat = Math.cos((lat * Math.PI) / 180.0);
  const sinLat = Math.sin((lat * Math.PI) / 180.0);
  const cosLon = Math.cos((lon * Math.PI) / 180.0);
  const sinLon = Math.sin((lon * Math.PI) / 180.0);
  const rad = 6378137.0;
  const f = 1.0 / 298.257224;
  const C =
    1.0 / Math.sqrt(cosLat * cosLat + (1 - f) * (1 - f) * sinLat * sinLat);
  const S = (1.0 - f) * (1.0 - f) * C;
  const h = alt;

  const x = (rad * C + h) * cosLat * cosLon;
  const y = (rad * C + h) * cosLat * sinLon;
  const z = (rad * S + h) * sinLat;

  return { x, y, z };
}

1 Ответ

2 голосов
/ 01 апреля 2019

Ваши шаги нормализации и растра отменяют необходимое вам масштабирование порта просмотра. Умножая это:

const xNormalized = (xPerspective + viewPort.width / 2) / viewPort.width;

дает вам:

const xNormalized = xPerspective / viewPort.width + 0.5;

И применяя эту строку:

const xRaster = Math.floor(xNormalized * viewPort.width);

дает вам:

const xRaster = Math.floor(xPerspective + viewPort.width * 0.5);

Ваш расчет xPerspective правильный (но см. Комментарий ниже) - однако значение будет около 1, если смотреть на ваши цифры. Именно поэтому точка находится рядом с центром экрана.

Правильный способ сделать это:

const xRaster = Math.floor(xPerspective * viewPort.width /2 + viewPort.width /2);

Вы можете упростить это. Идея состоит в том, что xPerspective - это tan угла, который xRelative составляет в глазу. Умножение tan на половину ширины экрана дает х расстояние от центра экрана. Затем вы добавляете положение x центра экрана, чтобы получить координаты экрана.

Ваша математика использует неявный вид с камеры, который выровнен по осям x, y, z. Чтобы переместить вид вокруг, вам нужно вычислить xRelative и т. Д. Относительно камеры перед выполнением шага деления перспективы (деление на zRelative). Простой способ сделать это - представить вашу камеру в виде 3 векторов, которые являются X, Y, Z вида камеры. Затем вы вычисляете проекцию вашей трехмерной точки на камеру, беря точечное произведение вектора [xRelative, yRelative, zRelative] с каждым из X, Y и Z. Это дает вам новое [xCamera, yCamera, zCamera], которое будет меняться при перемещении вашей камеры. Вы также можете сделать это с помощью матриц.

...